templar_vault_kernel/utils/
mod.rs

1//! Shared non-domain helpers for kernel-adjacent crates.
2
3use crate::types::TimestampNs;
4
5/// Generic readiness gate represented by an optional ready-at timestamp.
6///
7/// - `None` means ready immediately.
8/// - `Some(ts)` means ready when `now >= ts`.
9#[cfg_attr(not(target_arch = "wasm32"), derive(Debug))]
10#[derive(Clone, Copy, Default, PartialEq, Eq)]
11pub struct TimeGate {
12    ready_at_ns: Option<TimestampNs>,
13}
14
15impl TimeGate {
16    #[must_use]
17    pub const fn ready_now() -> Self {
18        Self { ready_at_ns: None }
19    }
20
21    #[must_use]
22    pub const fn from_ready_at(ready_at_ns: TimestampNs) -> Self {
23        Self {
24            ready_at_ns: Some(ready_at_ns),
25        }
26    }
27
28    #[must_use]
29    pub const fn schedule_from(now_ns: TimestampNs, delay_ns: TimestampNs) -> Self {
30        Self::from_ready_at(now_ns.saturating_add(delay_ns))
31    }
32
33    #[must_use]
34    pub const fn ready_at_ns(self) -> Option<TimestampNs> {
35        self.ready_at_ns
36    }
37
38    #[must_use]
39    pub fn is_ready(self, now_ns: TimestampNs) -> bool {
40        self.ready_at_ns.is_none_or(|ready_at| now_ns >= ready_at)
41    }
42
43    #[must_use]
44    pub fn remaining(self, now_ns: TimestampNs) -> TimestampNs {
45        self.ready_at_ns.map_or(TimestampNs::ZERO, |ready_at| {
46            ready_at.saturating_sub(now_ns)
47        })
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::TimeGate;
54    use crate::types::TimestampNs;
55
56    #[test]
57    fn time_gate_ready_now_is_always_ready() {
58        let gate = TimeGate::ready_now();
59        assert!(gate.is_ready(TimestampNs(0)));
60        assert!(gate.is_ready(TimestampNs(u64::MAX)));
61        assert_eq!(gate.remaining(TimestampNs(123)), TimestampNs(0));
62        assert_eq!(gate.ready_at_ns(), None);
63    }
64
65    #[test]
66    fn time_gate_scheduled_reports_remaining_and_readiness() {
67        let gate = TimeGate::schedule_from(TimestampNs(100), TimestampNs(50));
68        assert_eq!(gate.ready_at_ns(), Some(TimestampNs(150)));
69        assert!(!gate.is_ready(TimestampNs(149)));
70        assert!(gate.is_ready(TimestampNs(150)));
71        assert_eq!(gate.remaining(TimestampNs(120)), TimestampNs(30));
72        assert_eq!(gate.remaining(TimestampNs(160)), TimestampNs(0));
73    }
74}