templar_vault_kernel/utils/
mod.rs1use crate::types::TimestampNs;
4
5#[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}