Skip to main content

euv_ui/hook/throttled_value/
impl.rs

1use super::*;
2
3impl<T: Clone + PartialEq + Default + 'static> ThrottledValue<T> {
4    /// Sends `next` through the throttle.
5    ///
6    /// - If the throttle is idle (no cooldown active), the
7    ///   value is emitted immediately, and a cooldown
8    ///   window of `interval_ms` opens starting at `now`.
9    /// - If a cooldown is active, `next` is stored as the
10    ///   next pending value. The pending value will be
11    ///   committed on the next `tick` call once the
12    ///   cooldown expires. Intermediate `set` calls during
13    ///   the cooldown overwrite the pending slot — only
14    ///   the most recent input wins.
15    ///
16    /// `interval_ms = 0` collapses to "every `set` is
17    /// immediately committed" — the cooldown branch is
18    /// never taken.
19    ///
20    /// # Arguments
21    ///
22    /// - `T: Clone + PartialEq + Default + 'static` - A generic type parameter.
23    /// - `Instant` - A monotonic instant in time (`Instant`).
24    pub fn set(&self, next: T, now: Instant) {
25        if self.interval_ms == 0 {
26            self.get_value().set(next);
27            self.get_pending().set(None);
28            self.get_state().set(ThrottleState::Idle);
29            return;
30        }
31        match self.get_state().get() {
32            ThrottleState::Idle => {
33                self.get_value().set(next);
34                self.get_state().set(ThrottleState::Cooldown(now));
35            }
36            ThrottleState::Cooldown(_) => {
37                self.get_pending().set(Some(next));
38            }
39        }
40    }
41
42    /// Drives the throttle forward.
43    ///
44    /// - If idle, no-op.
45    /// - If a cooldown is active and the cooldown
46    ///   window has elapsed, any pending value is
47    ///   committed and the state returns to `Idle`. If
48    ///   nothing is pending, the state still returns to
49    ///   `Idle`. The cooldown simply lapses in that case.
50    ///
51    /// Returns `true` when a pending value was committed,
52    /// `false` otherwise.
53    ///
54    /// # Arguments
55    ///
56    /// - `Instant` - A monotonic instant in time (`Instant`).
57    ///
58    /// # Returns
59    ///
60    /// - `bool` - A boolean.
61    pub fn tick(&self, now: Instant) -> bool {
62        match self.get_state().get() {
63            ThrottleState::Idle => false,
64            ThrottleState::Cooldown(start) => {
65                if now.duration_since(start).as_millis() < u128::from(self.interval_ms) {
66                    return false;
67                }
68                let committed: bool = match self.get_pending().get() {
69                    Some(pending) => {
70                        self.get_value().set(pending);
71                        self.get_pending().set(None);
72                        true
73                    }
74                    None => false,
75                };
76                self.get_state().set(ThrottleState::Idle);
77                committed
78            }
79        }
80    }
81
82    /// Drops any pending value and ends the cooldown.
83    /// The emitted value is left untouched.
84    pub fn cancel(&self) {
85        self.get_pending().set(None);
86        self.get_state().set(ThrottleState::Idle);
87    }
88
89    /// Returns the currently emitted value as a snapshot.
90    ///
91    /// # Returns
92    ///
93    /// - `T` - The current value (or a snapshot thereof).
94    pub fn get(&self) -> T {
95        self.get_value().get()
96    }
97
98    /// Returns `true` when the throttle is in a cooldown
99    /// window (recent `set` that has not yet had a chance
100    /// to commit any pending follow-up).
101    ///
102    /// # Returns
103    ///
104    /// - `bool` - `true` when a throttle delay is active.
105    pub fn is_throttling(&self) -> bool {
106        matches!(self.get_state().get(), ThrottleState::Cooldown(_))
107    }
108}
109
110impl<T: Clone + PartialEq + Debug + Default + 'static> Display for ThrottledValue<T> {
111    /// Formats the [`ThrottledValue`] via the supplied formatter.
112    ///
113    /// # Arguments
114    ///
115    /// - `&mut Formatter<'_>` - The formatter receiving the formatted output.
116    ///
117    /// # Returns
118    ///
119    /// - `FmtResult` - Result of the formatting operation.
120    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
121        match &self.get_state().get() {
122            ThrottleState::Idle => {
123                write!(formatter, "ThrottledValue({:?})", self.get_value().get())
124            }
125            ThrottleState::Cooldown(_) => {
126                write!(
127                    formatter,
128                    "ThrottledValue(cooldown={:?})",
129                    self.get_value().get()
130                )
131            }
132        }
133    }
134}