Skip to main content

machi_types/
deadline.rs

1//! Absolute deadlines for cooperative cancellation.
2
3use std::time::{Duration, Instant};
4
5use serde::{Deserialize, Serialize};
6
7/// An absolute deadline for a unit of work.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct Deadline {
10    instant: Instant,
11}
12
13impl Deadline {
14    /// Deadline after `duration` from now.
15    #[must_use]
16    pub fn after(duration: Duration) -> Self {
17        Self {
18            instant: Instant::now() + duration,
19        }
20    }
21
22    /// Deadline at a specific instant.
23    #[must_use]
24    pub const fn at(instant: Instant) -> Self {
25        Self { instant }
26    }
27
28    /// Returns true when the deadline has passed.
29    #[must_use]
30    pub fn is_expired(&self) -> bool {
31        Instant::now() >= self.instant
32    }
33
34    /// Remaining time, or zero if expired.
35    #[must_use]
36    pub fn remaining(&self) -> Duration {
37        self.instant.saturating_duration_since(Instant::now())
38    }
39
40    /// Underlying instant.
41    #[must_use]
42    pub const fn instant(&self) -> Instant {
43        self.instant
44    }
45}
46
47/// Serializable duration budget (not wall-clock) for configs.
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
49pub struct DurationBudget {
50    /// Milliseconds.
51    pub millis: u64,
52}
53
54impl DurationBudget {
55    /// Construct from milliseconds.
56    #[must_use]
57    pub const fn from_millis(millis: u64) -> Self {
58        Self { millis }
59    }
60
61    /// Convert to [`Duration`].
62    #[must_use]
63    pub const fn as_duration(self) -> Duration {
64        Duration::from_millis(self.millis)
65    }
66
67    /// Convert to a live [`Deadline`].
68    #[must_use]
69    pub fn to_deadline(self) -> Deadline {
70        Deadline::after(self.as_duration())
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn expired_zero() {
80        let d = Deadline::after(Duration::ZERO);
81        assert!(d.remaining().is_zero() || d.is_expired());
82    }
83}