1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use super::*;
impl<T: Clone + PartialEq + Default + 'static> DebouncedValue<T> {
/// Schedules `next` to become the emitted value. The
/// commit happens on the next `tick` call at or after
/// `now + delay_ms`.
///
/// Calling `set` repeatedly within `delay_ms` of each
/// other means only the last value wins — that's the
/// "debounce" contract.
///
/// # Arguments
///
/// - `T: Clone + PartialEq + Default + 'static` - A generic type parameter.
/// - `Instant` - A monotonic instant in time (`Instant`).
pub fn set(&self, next: T, now: Instant) {
self.get_state().set(DebounceState::Pending(now, next));
}
/// Drives the state machine forward.
///
/// - If the state is `Idle`, this is a no-op.
/// - If the state is `Pending(set_at, value)`, this
/// emits `value` (writing it to the `value` signal)
/// iff `now - set_at >= delay_ms`. Otherwise the
/// pending value is preserved and the call returns
/// `false`.
///
/// Returns `true` when a pending value was emitted,
/// `false` otherwise.
///
/// # Arguments
///
/// - `Instant` - A monotonic instant in time (`Instant`).
///
/// # Returns
///
/// - `bool` - A boolean.
pub fn tick(&self, now: Instant) -> bool {
match self.get_state().get() {
DebounceState::Idle => false,
DebounceState::Pending(set_at, _) => {
if now.duration_since(set_at).as_millis() >= u128::from(self.delay_ms) {
let pending: T = match self.get_state().get() {
DebounceState::Pending(_, value) => value,
DebounceState::Idle => unreachable!(),
};
self.get_value().set(pending);
self.get_state().set(DebounceState::Idle);
true
} else {
false
}
}
}
}
/// Cancels any pending value without emitting it.
/// The emitted value is left untouched.
pub fn cancel(&self) {
self.get_state().set(DebounceState::Idle);
}
/// Returns the currently emitted value as a snapshot.
///
/// # Returns
///
/// - `T` - The current value (or a snapshot thereof).
pub fn get(&self) -> T {
self.get_value().get()
}
/// Returns `true` when a value is waiting to be
/// emitted.
///
/// # Returns
///
/// - `bool` - `true` when a value is waiting to be emitted.
pub fn is_pending(&self) -> bool {
matches!(self.get_state().get(), DebounceState::Pending(_, _))
}
}
impl<T: Clone + PartialEq + Debug + Default + 'static> Display for DebouncedValue<T> {
/// Formats the [`DebouncedValue`] via the supplied formatter.
///
/// # Arguments
///
/// - `&mut Formatter<'_>` - The formatter receiving the formatted output.
///
/// # Returns
///
/// - `FmtResult` - Result of the formatting operation.
fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
let pending: &DebounceState<T> = &self.get_state().get();
match pending {
DebounceState::Idle => {
write!(formatter, "DebouncedValue({:?})", self.get_value().get())
}
DebounceState::Pending(_, value) => {
write!(formatter, "DebouncedValue(pending={value:?})")
}
}
}
}