templar_curator_primitives/policy/cooldown/
mod.rs1use templar_vault_kernel::{TimeGate, TimestampNs};
8
9#[templar_vault_macros::vault_derive(borsh, serde, postcard)]
15#[derive(Clone, Default, PartialEq, Eq)]
16pub struct Cooldown {
17 pub last_event_ns: Option<u64>,
19 pub interval_ns: u64,
22}
23
24impl Cooldown {
25 fn gate(&self) -> TimeGate {
26 if self.is_unlimited() {
27 return TimeGate::ready_now();
28 }
29
30 match self.last_event_ns {
31 Some(last) => TimeGate::schedule_from(TimestampNs(last), TimestampNs(self.interval_ns)),
32 None => TimeGate::ready_now(),
33 }
34 }
35
36 #[must_use]
37 pub fn new(interval_ns: u64) -> Self {
38 Self {
39 last_event_ns: None,
40 interval_ns,
41 }
42 }
43
44 #[must_use]
45 pub fn unlimited() -> Self {
46 Self {
47 last_event_ns: None,
48 interval_ns: 0,
49 }
50 }
51
52 #[must_use]
53 pub fn is_unlimited(&self) -> bool {
54 self.interval_ns == 0
55 }
56
57 #[must_use]
64 pub fn is_ready(&self, current_ns: u64) -> bool {
65 self.gate().is_ready(TimestampNs(current_ns))
66 }
67
68 pub fn check(&self, current_ns: u64) -> Result<(), CooldownError> {
70 if self.is_ready(current_ns) {
71 Ok(())
72 } else {
73 Err(CooldownError::OnCooldown {
74 last_event_ns: self.last_event_ns,
75 interval_ns: self.interval_ns,
76 current_ns,
77 })
78 }
79 }
80
81 #[must_use]
82 pub fn record(&self, timestamp_ns: u64) -> Self {
83 Self {
84 last_event_ns: Some(timestamp_ns),
85 interval_ns: self.interval_ns,
86 }
87 }
88
89 #[must_use]
90 pub fn ready_at(&self) -> Option<u64> {
91 self.gate().ready_at_ns().map(Into::into)
92 }
93
94 #[must_use]
95 pub fn remaining(&self, current_ns: u64) -> u64 {
96 self.gate().remaining(TimestampNs(current_ns)).into()
97 }
98}
99
100#[templar_vault_macros::vault_derive]
102#[derive(Clone, PartialEq, Eq)]
103pub enum CooldownError {
104 OnCooldown {
106 last_event_ns: Option<u64>,
107 interval_ns: u64,
108 current_ns: u64,
109 },
110}