docs: bilingual comments for apsis-resource

Seventy-five comment blocks across the error kinds, the resource model
and the window algebra. The English is unchanged and the Chinese follows
under a blank doc line, as the glossary sets out.
This commit is contained in:
ApsisRS contributors 2026-10-02 01:00:22 +08:00
parent f5b1952555
commit f26a90bc96
5 changed files with 283 additions and 0 deletions

View File

@ -3,20 +3,34 @@
use core::fmt; use core::fmt;
/// The category of a resource error. /// The category of a resource error.
///
/// 资源错误的类别。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive] #[non_exhaustive]
pub enum ResourceErrorKind { pub enum ResourceErrorKind {
/// The end of an interval precedes its start. /// The end of an interval precedes its start.
///
/// 区间的终点早于起点。
ReversedInterval, ReversedInterval,
/// A time or duration is not finite. /// A time or duration is not finite.
///
/// 某时刻或时长非有限。
NotFinite, NotFinite,
/// A duration is negative. /// A duration is negative.
///
/// 时长为负。
NegativeDuration, NegativeDuration,
/// The capacity is not positive and finite. /// The capacity is not positive and finite.
///
/// 容量非正或非有限。
InvalidCapacity, InvalidCapacity,
/// No resource has the requested identifier. /// No resource has the requested identifier.
///
/// 没有资源具有所请求的标识符。
NoSuchResource, NoSuchResource,
/// A resource with the identifier already exists. /// A resource with the identifier already exists.
///
/// 具有该标识符的资源已存在。
DuplicateResource, DuplicateResource,
} }
@ -35,6 +49,8 @@ impl fmt::Display for ResourceErrorKind {
} }
/// A failure while building or querying a resource. /// A failure while building or querying a resource.
///
/// 构建或查询资源时发生的失败。
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResourceError { pub struct ResourceError {
kind: ResourceErrorKind, kind: ResourceErrorKind,
@ -43,22 +59,30 @@ pub struct ResourceError {
impl ResourceError { impl ResourceError {
/// Creates an error with the given category. /// Creates an error with the given category.
///
/// 用给定类别创建一个错误。
pub fn new(kind: ResourceErrorKind) -> Self { pub fn new(kind: ResourceErrorKind) -> Self {
Self { kind, detail: None } Self { kind, detail: None }
} }
/// Attaches a description of the values that caused the failure. /// Attaches a description of the values that caused the failure.
///
/// 附加对该失败所涉数值的说明。
pub fn with_detail(mut self, detail: impl Into<String>) -> Self { pub fn with_detail(mut self, detail: impl Into<String>) -> Self {
self.detail = Some(detail.into()); self.detail = Some(detail.into());
self self
} }
/// Returns the category of this failure. /// Returns the category of this failure.
///
/// 返回该失败的类别。
pub fn kind(&self) -> ResourceErrorKind { pub fn kind(&self) -> ResourceErrorKind {
self.kind self.kind
} }
/// Returns the detail text attached to this error, if any. /// Returns the detail text attached to this error, if any.
///
/// 返回附加在该错误上的说明文本,若有。
pub fn detail(&self) -> Option<&str> { pub fn detail(&self) -> Option<&str> {
self.detail.as_deref() self.detail.as_deref()
} }

View File

@ -8,16 +8,25 @@ use crate::window::{Interval, Window, merge_windows, subtract_window};
/// The identifier is an index chosen by the caller. It is deliberately opaque /// The identifier is an index chosen by the caller. It is deliberately opaque
/// to this crate so that a caller can use whatever numbering its own model /// to this crate so that a caller can use whatever numbering its own model
/// provides. /// provides.
///
/// 资源的标识符。
///
/// 标识符是由调用方选定的下标。它对本 crate 刻意保持不透明,以便调用方
/// 使用其自身模型所提供的任何编号方式。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ResourceId(pub usize); pub struct ResourceId(pub usize);
impl ResourceId { impl ResourceId {
/// Creates an identifier. /// Creates an identifier.
///
/// 创建一个标识符。
pub const fn new(index: usize) -> Self { pub const fn new(index: usize) -> Self {
Self(index) Self(index)
} }
/// Returns the index. /// Returns the index.
///
/// 返回该下标。
pub const fn index(self) -> usize { pub const fn index(self) -> usize {
self.0 self.0
} }
@ -35,17 +44,31 @@ impl core::fmt::Display for ResourceId {
/// that occupies more than the capacity of a resource cannot be scheduled on /// that occupies more than the capacity of a resource cannot be scheduled on
/// it at all; two tasks conflict when their demands together exceed the /// it at all; two tasks conflict when their demands together exceed the
/// capacity at some instant. /// capacity at some instant.
///
/// 带有可用窗口与容量的资源。
///
/// 容量是由调用方选定单位的正数。占用量超过某资源容量的任务根本无法在其上
/// 调度;当两个任务的需求之和在某瞬超出容量时,二者冲突。
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Resource { pub struct Resource {
/// The identifier of this resource. /// The identifier of this resource.
///
/// 该资源的标识符。
pub id: ResourceId, pub id: ResourceId,
/// The capacity, in a unit the caller chooses. /// The capacity, in a unit the caller chooses.
/// ///
/// A capacity of one with unit demands gives an exclusive resource, where /// A capacity of one with unit demands gives an exclusive resource, where
/// no two tasks overlap. A larger capacity, or fractional demands, gives /// no two tasks overlap. A larger capacity, or fractional demands, gives
/// a shared resource. /// a shared resource.
///
/// 容量,单位由调用方选择。
///
/// 容量为一且需求为单位量时得到独占资源,任意两个任务都不重叠。更大的
/// 容量或分数需求则得到共享资源。
capacity: f64, capacity: f64,
/// The availability windows, kept merged and in time order. /// The availability windows, kept merged and in time order.
///
/// 可用窗口,保持合并且按时间有序。
windows: Vec<Window>, windows: Vec<Window>,
} }
@ -56,6 +79,11 @@ impl Resource {
/// windows before using it. That is the safe default: a resource whose /// windows before using it. That is the safe default: a resource whose
/// availability was forgotten is unavailable rather than always /// availability was forgotten is unavailable rather than always
/// available. /// available.
///
/// 创建无可用性、容量给定的资源。
///
/// 没有窗口的资源永不可用,故调用方须先添加窗口再使用。这是安全的
/// 默认值:忘记设置可用性的资源是不可用,而非始终可用。
pub const fn new(id: ResourceId, capacity: f64) -> Self { pub const fn new(id: ResourceId, capacity: f64) -> Self {
Self { Self {
id, id,
@ -65,6 +93,8 @@ impl Resource {
} }
/// Returns the capacity. /// Returns the capacity.
///
/// 返回该容量。
pub const fn capacity(&self) -> f64 { pub const fn capacity(&self) -> f64 {
self.capacity self.capacity
} }
@ -72,6 +102,10 @@ impl Resource {
/// Sets the capacity. /// Sets the capacity.
/// ///
/// Returns an error when the capacity is not positive and finite. /// Returns an error when the capacity is not positive and finite.
///
/// 设置容量。
///
/// 当容量非正或非有限时返回错误。
pub fn set_capacity(&mut self, capacity: f64) -> Result<(), ResourceError> { pub fn set_capacity(&mut self, capacity: f64) -> Result<(), ResourceError> {
if !capacity.is_finite() || capacity <= 0.0 { if !capacity.is_finite() || capacity <= 0.0 {
return Err(ResourceError::new(ResourceErrorKind::InvalidCapacity) return Err(ResourceError::new(ResourceErrorKind::InvalidCapacity)
@ -82,21 +116,29 @@ impl Resource {
} }
/// Returns `true` when the capacity is usable. /// Returns `true` when the capacity is usable.
///
/// 当该容量可用时返回 `true`。
pub fn has_valid_capacity(&self) -> bool { pub fn has_valid_capacity(&self) -> bool {
self.capacity.is_finite() && self.capacity > 0.0 self.capacity.is_finite() && self.capacity > 0.0
} }
/// Returns the availability windows, merged and in time order. /// Returns the availability windows, merged and in time order.
///
/// 返回可用窗口,已合并且按时间有序。
pub fn windows(&self) -> &[Window] { pub fn windows(&self) -> &[Window] {
&self.windows &self.windows
} }
/// Returns the number of availability windows. /// Returns the number of availability windows.
///
/// 返回可用窗口的数量。
pub fn window_count(&self) -> usize { pub fn window_count(&self) -> usize {
self.windows.len() self.windows.len()
} }
/// Returns `true` when the resource has no availability at all. /// Returns `true` when the resource has no availability at all.
///
/// 当该资源完全没有可用性时返回 `true`。
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.windows.is_empty() self.windows.is_empty()
} }
@ -107,6 +149,12 @@ impl Resource {
/// the caller supplies them in. /// the caller supplies them in.
/// ///
/// Returns an error when a window is not usable. /// Returns an error when a window is not usable.
///
/// 用一组窗口替换可用性。
///
/// 窗口会被合并,故无论调用方以何种顺序给出,存储形式都是规范的。
///
/// 当某个窗口不可用时返回错误。
pub fn set_windows(&mut self, windows: &[Window]) -> Result<(), ResourceError> { pub fn set_windows(&mut self, windows: &[Window]) -> Result<(), ResourceError> {
self.windows = merge_windows(windows)?; self.windows = merge_windows(windows)?;
Ok(()) Ok(())
@ -117,6 +165,12 @@ impl Resource {
/// The window is merged into the existing set. /// The window is merged into the existing set.
/// ///
/// Returns an error when the window is not usable. /// Returns an error when the window is not usable.
///
/// 添加一个可用窗口。
///
/// 该窗口被合并进已有集合。
///
/// 当该窗口不可用时返回错误。
pub fn add_window(&mut self, window: Window) -> Result<(), ResourceError> { pub fn add_window(&mut self, window: Window) -> Result<(), ResourceError> {
window.validate()?; window.validate()?;
let mut all = self.windows.clone(); let mut all = self.windows.clone();
@ -131,6 +185,12 @@ impl Resource {
/// stored window in two. /// stored window in two.
/// ///
/// Returns an error when the window is not usable. /// Returns an error when the window is not usable.
///
/// 移除一个可用窗口。
///
/// 该窗口从已有集合中被减去,可能把某个已存窗口一分为二。
///
/// 当该窗口不可用时返回错误。
pub fn remove_window(&mut self, removed: Window) -> Result<(), ResourceError> { pub fn remove_window(&mut self, removed: Window) -> Result<(), ResourceError> {
self.windows = subtract_window(&self.windows, removed)?; self.windows = subtract_window(&self.windows, removed)?;
Ok(()) Ok(())
@ -141,6 +201,11 @@ impl Resource {
/// An instantaneous query is a query of zero duration, which is satisfied /// An instantaneous query is a query of zero duration, which is satisfied
/// when the instant is inside a window. At the end of a window the /// when the instant is inside a window. At the end of a window the
/// resource is not available, because the window is half-open. /// resource is not available, because the window is half-open.
///
/// 当该资源在某瞬可用时返回 `true`。
///
/// 瞬时查询即零时长查询,当该瞬落在某个窗口内即得到满足。在窗口
/// 终点处该资源不可用,因为窗口是半开的。
pub fn is_available_at(&self, time: f64) -> bool { pub fn is_available_at(&self, time: f64) -> bool {
self.windows self.windows
.iter() .iter()
@ -152,6 +217,10 @@ impl Resource {
/// A resource can satisfy a request of zero duration whenever it is /// A resource can satisfy a request of zero duration whenever it is
/// available at the requested instant, so a zero duration is checked as /// available at the requested instant, so a zero duration is checked as
/// an instant. /// an instant.
///
/// 当该资源在整段区间上可用时返回 `true`。
///
/// 只要资源在所请求瞬时可用,即可满足零时长请求,故零时长按瞬时检查。
pub fn is_available_over(&self, start: f64, duration: f64) -> bool { pub fn is_available_over(&self, start: f64, duration: f64) -> bool {
if !start.is_finite() || !duration.is_finite() || duration < 0.0 { if !start.is_finite() || !duration.is_finite() || duration < 0.0 {
return false; return false;
@ -170,6 +239,11 @@ impl Resource {
/// The result is the intersection of the stored windows with the query, /// The result is the intersection of the stored windows with the query,
/// which is what a scheduler needs to know when it asks what part of its /// which is what a scheduler needs to know when it asks what part of its
/// planning horizon a resource is usable for. /// planning horizon a resource is usable for.
///
/// 返回落在某区间内的可用性。
///
/// 结果是已存窗口与该查询的交集,这正是调度器在询问资源的规划时域中
/// 哪一段可用时所需的信息。
pub fn availability_within(&self, query: Interval) -> Vec<Window> { pub fn availability_within(&self, query: Interval) -> Vec<Window> {
self.windows self.windows
.iter() .iter()
@ -181,6 +255,10 @@ impl Resource {
/// Returns the total availability, in seconds. /// Returns the total availability, in seconds.
/// ///
/// The windows are disjoint, so the durations add. /// The windows are disjoint, so the durations add.
///
/// 返回总可用时长,单位为秒。
///
/// 各窗口互不相交,故时长可直接相加。
pub fn total_availability(&self) -> f64 { pub fn total_availability(&self) -> f64 {
self.windows.iter().map(Window::duration).sum() self.windows.iter().map(Window::duration).sum()
} }
@ -190,6 +268,11 @@ impl Resource {
/// This is the earliest time at which the resource can satisfy a request /// This is the earliest time at which the resource can satisfy a request
/// of the given duration, which is what a greedy scheduler asks. Returns /// of the given duration, which is what a greedy scheduler asks. Returns
/// `None` when no window is long enough. /// `None` when no window is long enough.
///
/// 返回第一个能容纳某请求的窗口。
///
/// 这是该资源能满足给定时长请求的最早时刻,即贪心调度器所询问的量。
/// 没有足够长的窗口时返回 `None`。
pub fn earliest_fit(&self, duration: f64) -> Option<Window> { pub fn earliest_fit(&self, duration: f64) -> Option<Window> {
if !duration.is_finite() || duration < 0.0 { if !duration.is_finite() || duration < 0.0 {
return None; return None;
@ -204,6 +287,10 @@ impl Resource {
/// than a given time. /// than a given time.
/// ///
/// Returns `None` when no window is both late enough and long enough. /// Returns `None` when no window is both late enough and long enough.
///
/// 返回第一个能容纳请求的窗口,且该请求的起点不早于给定时刻。
///
/// 没有既够晚又够长的窗口时返回 `None`。
pub fn earliest_fit_after(&self, not_before: f64, duration: f64) -> Option<Window> { pub fn earliest_fit_after(&self, not_before: f64, duration: f64) -> Option<Window> {
if !not_before.is_finite() || !duration.is_finite() || duration < 0.0 { if !not_before.is_finite() || !duration.is_finite() || duration < 0.0 {
return None; return None;
@ -219,6 +306,10 @@ impl Resource {
/// ///
/// Returns `None` when the resource is not available at the given /// Returns `None` when the resource is not available at the given
/// instant, and infinity when the window that contains it is unbounded. /// instant, and infinity when the window that contains it is unbounded.
///
/// 返回该资源不再可用的时刻。
///
/// 该资源在所给瞬时不可用时返回 `None`;包含该瞬的窗口无界时返回无穷。
pub fn busy_until(&self, time: f64) -> Option<f64> { pub fn busy_until(&self, time: f64) -> Option<f64> {
self.windows self.windows
.iter() .iter()
@ -232,6 +323,11 @@ impl Resource {
/// The set keeps the resources in the order they were added and provides the /// The set keeps the resources in the order they were added and provides the
/// lookups a scheduler needs: by identifier, and by the resource that has a /// lookups a scheduler needs: by identifier, and by the resource that has a
/// window covering an instant. /// window covering an instant.
///
/// 以标识符为索引的资源集合。
///
/// 该集合按添加顺序保存资源,并提供调度器所需的查找:按标识符,以及按
/// 某个瞬时上有窗口覆盖的资源。
#[derive(Debug, Clone, PartialEq, Default)] #[derive(Debug, Clone, PartialEq, Default)]
pub struct ResourceSet { pub struct ResourceSet {
resources: Vec<Resource>, resources: Vec<Resource>,
@ -239,6 +335,8 @@ pub struct ResourceSet {
impl ResourceSet { impl ResourceSet {
/// Creates an empty set. /// Creates an empty set.
///
/// 创建一个空集合。
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
resources: Vec::new(), resources: Vec::new(),
@ -249,6 +347,10 @@ impl ResourceSet {
/// ///
/// Returns an error when two resources share an identifier, because a /// Returns an error when two resources share an identifier, because a
/// lookup would then be ambiguous. /// lookup would then be ambiguous.
///
/// 由一组资源创建集合。
///
/// 两个资源标识符相同时返回错误,因为查找将有歧义。
pub fn from_resources(resources: Vec<Resource>) -> Result<Self, ResourceError> { pub fn from_resources(resources: Vec<Resource>) -> Result<Self, ResourceError> {
let mut set = Self::new(); let mut set = Self::new();
for resource in resources { for resource in resources {
@ -260,6 +362,10 @@ impl ResourceSet {
/// Inserts a resource. /// Inserts a resource.
/// ///
/// Returns an error when the identifier is already present. /// Returns an error when the identifier is already present.
///
/// 插入一个资源。
///
/// 该标识符已存在时返回错误。
pub fn insert(&mut self, resource: Resource) -> Result<(), ResourceError> { pub fn insert(&mut self, resource: Resource) -> Result<(), ResourceError> {
if self.get(resource.id).is_some() { if self.get(resource.id).is_some() {
return Err(ResourceError::new(ResourceErrorKind::DuplicateResource) return Err(ResourceError::new(ResourceErrorKind::DuplicateResource)
@ -270,11 +376,15 @@ impl ResourceSet {
} }
/// Returns the resource with the given identifier. /// Returns the resource with the given identifier.
///
/// 返回具有所给标识符的资源。
pub fn get(&self, id: ResourceId) -> Option<&Resource> { pub fn get(&self, id: ResourceId) -> Option<&Resource> {
self.resources.iter().find(|resource| resource.id == id) self.resources.iter().find(|resource| resource.id == id)
} }
/// Returns the resource with the given identifier for modification. /// Returns the resource with the given identifier for modification.
///
/// 返回具有所给标识符的资源以供修改。
pub fn get_mut(&mut self, id: ResourceId) -> Option<&mut Resource> { pub fn get_mut(&mut self, id: ResourceId) -> Option<&mut Resource> {
self.resources.iter_mut().find(|resource| resource.id == id) self.resources.iter_mut().find(|resource| resource.id == id)
} }
@ -282,6 +392,10 @@ impl ResourceSet {
/// Removes a resource. /// Removes a resource.
/// ///
/// Returns an error when no resource has the identifier. /// Returns an error when no resource has the identifier.
///
/// 移除一个资源。
///
/// 没有资源具有该标识符时返回错误。
pub fn remove(&mut self, id: ResourceId) -> Result<Resource, ResourceError> { pub fn remove(&mut self, id: ResourceId) -> Result<Resource, ResourceError> {
let index = self let index = self
.resources .resources
@ -294,16 +408,22 @@ impl ResourceSet {
} }
/// Returns the resources in the order they were added. /// Returns the resources in the order they were added.
///
/// 返回按添加顺序排列的资源。
pub fn resources(&self) -> &[Resource] { pub fn resources(&self) -> &[Resource] {
&self.resources &self.resources
} }
/// Returns the number of resources. /// Returns the number of resources.
///
/// 返回资源数量。
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.resources.len() self.resources.len()
} }
/// Returns `true` when there are no resources. /// Returns `true` when there are no resources.
///
/// 当集合中没有资源时返回 `true`。
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.resources.is_empty() self.resources.is_empty()
} }
@ -311,6 +431,10 @@ impl ResourceSet {
/// Returns the identifiers of the resources available at an instant. /// Returns the identifiers of the resources available at an instant.
/// ///
/// The result is in the order the resources were added. /// The result is in the order the resources were added.
///
/// 返回在某瞬可用的资源标识符。
///
/// 结果按资源添加顺序排列。
pub fn available_at(&self, time: f64) -> Vec<ResourceId> { pub fn available_at(&self, time: f64) -> Vec<ResourceId> {
self.resources self.resources
.iter() .iter()
@ -321,6 +445,8 @@ impl ResourceSet {
/// Returns the identifiers of the resources available for a whole /// Returns the identifiers of the resources available for a whole
/// interval. /// interval.
///
/// 返回在整段区间上可用的资源标识符。
pub fn available_over(&self, start: f64, duration: f64) -> Vec<ResourceId> { pub fn available_over(&self, start: f64, duration: f64) -> Vec<ResourceId> {
self.resources self.resources
.iter() .iter()
@ -330,6 +456,8 @@ impl ResourceSet {
} }
/// Returns the total availability of every resource, in seconds. /// Returns the total availability of every resource, in seconds.
///
/// 返回每个资源的总可用时长,单位为秒。
pub fn total_availability(&self) -> f64 { pub fn total_availability(&self) -> f64 {
self.resources self.resources
.iter() .iter()

View File

@ -10,16 +10,28 @@ use core::fmt;
/// instant at the boundary, which belongs to the second. Two intervals are /// instant at the boundary, which belongs to the second. Two intervals are
/// therefore disjoint when the end of the first is at or before the start of /// therefore disjoint when the end of the first is at or before the start of
/// the second. /// the second.
///
/// 半开时间区间 `[start, end)`。
///
/// 采用半开约定正是为了使区间既不重叠也无空隙地铺满:一个在某处结束、
/// 另一处从此开始的区间只在边界那一瞬相交,而该瞬归后者。故当第一个区
/// 间的终点不晚于第二个区间的起点时,二者不相交。
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct Interval { pub struct Interval {
/// The start of the interval, in seconds. /// The start of the interval, in seconds.
///
/// 区间起点,单位为秒。
pub start: f64, pub start: f64,
/// The end of the interval, in seconds. /// The end of the interval, in seconds.
///
/// 区间终点,单位为秒。
pub end: f64, pub end: f64,
} }
impl Interval { impl Interval {
/// Creates an interval. /// Creates an interval.
///
/// 创建一个区间。
pub const fn new(start: f64, end: f64) -> Self { pub const fn new(start: f64, end: f64) -> Self {
Self { start, end } Self { start, end }
} }
@ -27,16 +39,24 @@ impl Interval {
/// Returns the duration of the interval, in seconds. /// Returns the duration of the interval, in seconds.
/// ///
/// The value is zero for an empty interval and positive otherwise. /// The value is zero for an empty interval and positive otherwise.
///
/// 返回该区间的时长,单位为秒。
///
/// 对空区间该值为零,其余情形为正。
pub fn duration(&self) -> f64 { pub fn duration(&self) -> f64 {
(self.end - self.start).max(0.0) (self.end - self.start).max(0.0)
} }
/// Returns `true` when the interval contains no time. /// Returns `true` when the interval contains no time.
///
/// 当该区间不含任何时刻时返回 `true`。
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.end <= self.start self.end <= self.start
} }
/// Returns `true` when the interval is finite and ordered. /// Returns `true` when the interval is finite and ordered.
///
/// 当该区间有限且次序正确时返回 `true`。
pub fn is_valid(&self) -> bool { pub fn is_valid(&self) -> bool {
self.start.is_finite() && self.end.is_finite() && self.end >= self.start self.start.is_finite() && self.end.is_finite() && self.end >= self.start
} }
@ -45,6 +65,10 @@ impl Interval {
/// ///
/// The start is included and the end is excluded, so a boundary instant /// The start is included and the end is excluded, so a boundary instant
/// belongs to the interval that begins there. /// belongs to the interval that begins there.
///
/// 当某时刻落在该区间内时返回 `true`。
///
/// 含起点不含终点,故边界时刻归由此处开始的区间。
pub fn contains(&self, time: f64) -> bool { pub fn contains(&self, time: f64) -> bool {
time >= self.start && time < self.end time >= self.start && time < self.end
} }
@ -53,6 +77,10 @@ impl Interval {
/// ///
/// An empty interval is contained by any interval that contains its /// An empty interval is contained by any interval that contains its
/// instant, so a zero length query inside the range is satisfied. /// instant, so a zero length query inside the range is satisfied.
///
/// 当该区间完全包含另一区间时返回 `true`。
///
/// 空区间被任何包含其瞬时的区间所包含,故范围内的零长度查询得到满足。
pub fn contains_interval(&self, other: &Self) -> bool { pub fn contains_interval(&self, other: &Self) -> bool {
if other.is_empty() { if other.is_empty() {
return other.start >= self.start && other.start <= self.end; return other.start >= self.start && other.start <= self.end;
@ -64,12 +92,18 @@ impl Interval {
/// ///
/// Two intervals that meet at a point do not overlap, because the point /// Two intervals that meet at a point do not overlap, because the point
/// belongs to only one of them under the half-open convention. /// belongs to only one of them under the half-open convention.
///
/// 当两个区间共享任一时刻时返回 `true`。
///
/// 在一点相接的两个区间不重叠,因为在半开约定下该点只属于其中之一。
pub fn overlaps(&self, other: &Self) -> bool { pub fn overlaps(&self, other: &Self) -> bool {
self.start < other.end && other.start < self.end self.start < other.end && other.start < self.end
} }
/// Returns the intersection of two intervals, or `None` when they do not /// Returns the intersection of two intervals, or `None` when they do not
/// overlap. /// overlap.
///
/// 返回两个区间的交集;二者不重叠时返回 `None`。
pub fn intersection(&self, other: &Self) -> Option<Self> { pub fn intersection(&self, other: &Self) -> Option<Self> {
if !self.overlaps(other) { if !self.overlaps(other) {
return None; return None;
@ -81,12 +115,16 @@ impl Interval {
} }
/// Returns the shortest interval containing both. /// Returns the shortest interval containing both.
///
/// 返回包含二者的最短区间。
pub fn hull(&self, other: &Self) -> Self { pub fn hull(&self, other: &Self) -> Self {
Self::new(self.start.min(other.start), self.end.max(other.end)) Self::new(self.start.min(other.start), self.end.max(other.end))
} }
/// Returns `true` when the two intervals touch or overlap, so that they /// Returns `true` when the two intervals touch or overlap, so that they
/// can be merged without leaving a gap. /// can be merged without leaving a gap.
///
/// 当两个区间相接或重叠时返回 `true`,此时合并它们不会留下空隙。
pub fn is_adjacent_to(&self, other: &Self) -> bool { pub fn is_adjacent_to(&self, other: &Self) -> bool {
self.start <= other.end && other.start <= self.end self.start <= other.end && other.start <= self.end
} }
@ -102,14 +140,23 @@ impl fmt::Display for Interval {
/// ///
/// This is an [`Interval`] under a name that reads better where the caller is /// This is an [`Interval`] under a name that reads better where the caller is
/// describing when a resource can be used rather than doing interval algebra. /// describing when a resource can be used rather than doing interval algebra.
///
/// 资源的可用窗口。
///
/// 它就是一个 [`Interval`],只是当调用方描述资源何时可用、而非进行区间运算
/// 时,这个名字读起来更贴切。
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct Window { pub struct Window {
/// The interval during which the resource is available. /// The interval during which the resource is available.
///
/// 资源可用的时间区间。
pub interval: Interval, pub interval: Interval,
} }
impl Window { impl Window {
/// Creates a window from a start and an end time. /// Creates a window from a start and an end time.
///
/// 由起始时刻与结束时刻创建窗口。
pub const fn new(start: f64, end: f64) -> Self { pub const fn new(start: f64, end: f64) -> Self {
Self { Self {
interval: Interval::new(start, end), interval: Interval::new(start, end),
@ -117,31 +164,43 @@ impl Window {
} }
/// Creates a window from an interval. /// Creates a window from an interval.
///
/// 由区间创建窗口。
pub const fn from_interval(interval: Interval) -> Self { pub const fn from_interval(interval: Interval) -> Self {
Self { interval } Self { interval }
} }
/// Returns the start of the window, in seconds. /// Returns the start of the window, in seconds.
///
/// 返回窗口起点,单位为秒。
pub const fn start(&self) -> f64 { pub const fn start(&self) -> f64 {
self.interval.start self.interval.start
} }
/// Returns the end of the window, in seconds. /// Returns the end of the window, in seconds.
///
/// 返回窗口终点,单位为秒。
pub const fn end(&self) -> f64 { pub const fn end(&self) -> f64 {
self.interval.end self.interval.end
} }
/// Returns the duration of the window, in seconds. /// Returns the duration of the window, in seconds.
///
/// 返回窗口时长,单位为秒。
pub fn duration(&self) -> f64 { pub fn duration(&self) -> f64 {
self.interval.duration() self.interval.duration()
} }
/// Returns `true` when the window is usable. /// Returns `true` when the window is usable.
///
/// 当该窗口可用时返回 `true`。
pub fn is_valid(&self) -> bool { pub fn is_valid(&self) -> bool {
self.interval.is_valid() self.interval.is_valid()
} }
/// Validates the window, returning the reason when it is not usable. /// Validates the window, returning the reason when it is not usable.
///
/// 校验该窗口;不可用时返回原因。
pub fn validate(&self) -> Result<(), ResourceError> { pub fn validate(&self) -> Result<(), ResourceError> {
if !self.interval.start.is_finite() || !self.interval.end.is_finite() { if !self.interval.start.is_finite() || !self.interval.end.is_finite() {
return Err(ResourceError::new(ResourceErrorKind::NotFinite) return Err(ResourceError::new(ResourceErrorKind::NotFinite)
@ -168,6 +227,13 @@ impl fmt::Display for Window {
/// disjoint, and non-empty, so that two sets can be compared directly. /// disjoint, and non-empty, so that two sets can be compared directly.
/// ///
/// Returns an error when a window is not usable. /// Returns an error when a window is not usable.
///
/// 把一组窗口合并为按时间排序的互不相交窗口。
///
/// 重叠或相接的窗口被合并,空窗口被丢弃。结果是一组可用窗口的规范形式:
/// 有序、互不相交且无空项,故两组可直接比较。
///
/// 当某个窗口不可用时返回错误。
pub fn merge_windows(windows: &[Window]) -> Result<Vec<Window>, ResourceError> { pub fn merge_windows(windows: &[Window]) -> Result<Vec<Window>, ResourceError> {
for window in windows { for window in windows {
window.validate()?; window.validate()?;
@ -182,6 +248,7 @@ pub fn merge_windows(windows: &[Window]) -> Result<Vec<Window>, ResourceError> {
} }
// Sorting on the start and then the end gives a deterministic order for // Sorting on the start and then the end gives a deterministic order for
// intervals with equal starts. // intervals with equal starts.
// 先按起点再按终点排序,使起点相同的区间具有确定次序。
intervals.sort_by(|first, second| { intervals.sort_by(|first, second| {
first first
.start .start
@ -214,6 +281,13 @@ pub fn merge_windows(windows: &[Window]) -> Result<Vec<Window>, ResourceError> {
/// that contains it is split in two. /// that contains it is split in two.
/// ///
/// Returns an error when any input window is not usable. /// Returns an error when any input window is not usable.
///
/// 从一组窗口中逐个减去某个窗口。
///
/// 结果是同一组可用性去掉该窗口之后的情形。完全落在被减窗口内部的窗口
/// 消失,包含它的窗口被一分为二。
///
/// 当任一输入窗口不可用时返回错误。
pub fn subtract_window(windows: &[Window], removed: Window) -> Result<Vec<Window>, ResourceError> { pub fn subtract_window(windows: &[Window], removed: Window) -> Result<Vec<Window>, ResourceError> {
removed.validate()?; removed.validate()?;
let merged = merge_windows(windows)?; let merged = merge_windows(windows)?;
@ -227,6 +301,7 @@ pub fn subtract_window(windows: &[Window], removed: Window) -> Result<Vec<Window
None => result.push(window), None => result.push(window),
Some(overlap) => { Some(overlap) => {
// The part before the removed window, and the part after it. // The part before the removed window, and the part after it.
// 被减窗口之前的一段,以及之后的一段。
if overlap.start > window.interval.start { if overlap.start > window.interval.start {
result.push(Window::new(window.interval.start, overlap.start)); result.push(Window::new(window.interval.start, overlap.start));
} }

View File

@ -3,22 +3,38 @@
use core::fmt; use core::fmt;
/// The category of a schedule error. /// The category of a schedule error.
///
/// 计划错误的类别。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive] #[non_exhaustive]
pub enum ScheduleErrorKind { pub enum ScheduleErrorKind {
/// The start time is not finite. /// The start time is not finite.
///
/// 起始时刻非有限。
InvalidStart, InvalidStart,
/// The task is already placed. /// The task is already placed.
///
/// 该任务已被放置。
AlreadyPlaced, AlreadyPlaced,
/// The task is not placed. /// The task is not placed.
///
/// 该任务未被放置。
NotPlaced, NotPlaced,
/// The placement starts before the task's window opens. /// The placement starts before the task's window opens.
///
/// 放置早于该任务窗口的开启时刻。
BeforeWindow, BeforeWindow,
/// The placement ends after the task's window closes. /// The placement ends after the task's window closes.
///
/// 放置晚于该任务窗口的关闭时刻。
AfterWindow, AfterWindow,
/// The placement does not use the resources the task requires. /// The placement does not use the resources the task requires.
///
/// 放置未使用该任务所要求的资源。
RequirementMismatch, RequirementMismatch,
/// A resource is used beyond its capacity. /// A resource is used beyond its capacity.
///
/// 某资源的使用超出其容量。
CapacityExceeded, CapacityExceeded,
} }
@ -38,6 +54,8 @@ impl fmt::Display for ScheduleErrorKind {
} }
/// A failure while building or evaluating a schedule. /// A failure while building or evaluating a schedule.
///
/// 构建或评估计划时发生的失败。
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScheduleError { pub struct ScheduleError {
kind: ScheduleErrorKind, kind: ScheduleErrorKind,
@ -46,22 +64,30 @@ pub struct ScheduleError {
impl ScheduleError { impl ScheduleError {
/// Creates an error with the given category. /// Creates an error with the given category.
///
/// 用给定类别创建一个错误。
pub fn new(kind: ScheduleErrorKind) -> Self { pub fn new(kind: ScheduleErrorKind) -> Self {
Self { kind, detail: None } Self { kind, detail: None }
} }
/// Attaches a description of the values that caused the failure. /// Attaches a description of the values that caused the failure.
///
/// 附加对该失败所涉数值的说明。
pub fn with_detail(mut self, detail: impl Into<String>) -> Self { pub fn with_detail(mut self, detail: impl Into<String>) -> Self {
self.detail = Some(detail.into()); self.detail = Some(detail.into());
self self
} }
/// Returns the category of this failure. /// Returns the category of this failure.
///
/// 返回该失败的类别。
pub fn kind(&self) -> ScheduleErrorKind { pub fn kind(&self) -> ScheduleErrorKind {
self.kind self.kind
} }
/// Returns the detail text attached to this error, if any. /// Returns the detail text attached to this error, if any.
///
/// 返回附加在该错误上的说明文本,若有。
pub fn detail(&self) -> Option<&str> { pub fn detail(&self) -> Option<&str> {
self.detail.as_deref() self.detail.as_deref()
} }

View File

@ -3,26 +3,46 @@
use core::fmt; use core::fmt;
/// The category of a task error. /// The category of a task error.
///
/// 任务错误的类别。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive] #[non_exhaustive]
pub enum TaskErrorKind { pub enum TaskErrorKind {
/// The duration is not finite, or is negative. /// The duration is not finite, or is negative.
///
/// 时长非有限或为负。
InvalidDuration, InvalidDuration,
/// A time window is not finite or ends before it starts. /// A time window is not finite or ends before it starts.
///
/// 时间窗口非有限或终点早于起点。
InvalidWindow, InvalidWindow,
/// The window is shorter than the task duration. /// The window is shorter than the task duration.
///
/// 窗口短于任务时长。
WindowTooShort, WindowTooShort,
/// A resource demand is not positive and finite. /// A resource demand is not positive and finite.
///
/// 资源需求非正或非有限。
InvalidDemand, InvalidDemand,
/// The same resource is required more than once. /// The same resource is required more than once.
///
/// 同一资源被重复要求。
DuplicateRequirement, DuplicateRequirement,
/// A priority or value is not finite. /// A priority or value is not finite.
///
/// 优先级或某个数值非有限。
InvalidWeight, InvalidWeight,
/// No task has the requested identifier. /// No task has the requested identifier.
///
/// 没有任务具有所请求的标识符。
NoSuchTask, NoSuchTask,
/// A task with the identifier already exists. /// A task with the identifier already exists.
///
/// 具有该标识符的任务已存在。
DuplicateTask, DuplicateTask,
/// A precedence constraint would close a cycle. /// A precedence constraint would close a cycle.
///
/// 某优先约束会闭成环。
PrecedenceCycle, PrecedenceCycle,
} }
@ -44,6 +64,8 @@ impl fmt::Display for TaskErrorKind {
} }
/// A failure while building or querying a task. /// A failure while building or querying a task.
///
/// 构建或查询任务时发生的失败。
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct TaskError { pub struct TaskError {
kind: TaskErrorKind, kind: TaskErrorKind,
@ -52,22 +74,30 @@ pub struct TaskError {
impl TaskError { impl TaskError {
/// Creates an error with the given category. /// Creates an error with the given category.
///
/// 用给定类别创建一个错误。
pub fn new(kind: TaskErrorKind) -> Self { pub fn new(kind: TaskErrorKind) -> Self {
Self { kind, detail: None } Self { kind, detail: None }
} }
/// Attaches a description of the values that caused the failure. /// Attaches a description of the values that caused the failure.
///
/// 附加对该失败所涉数值的说明。
pub fn with_detail(mut self, detail: impl Into<String>) -> Self { pub fn with_detail(mut self, detail: impl Into<String>) -> Self {
self.detail = Some(detail.into()); self.detail = Some(detail.into());
self self
} }
/// Returns the category of this failure. /// Returns the category of this failure.
///
/// 返回该失败的类别。
pub fn kind(&self) -> TaskErrorKind { pub fn kind(&self) -> TaskErrorKind {
self.kind self.kind
} }
/// Returns the detail text attached to this error, if any. /// Returns the detail text attached to this error, if any.
///
/// 返回附加在该错误上的说明文本,若有。
pub fn detail(&self) -> Option<&str> { pub fn detail(&self) -> Option<&str> {
self.detail.as_deref() self.detail.as_deref()
} }