debounce/
buffer.rs

1use std::cmp::Ordering;
2use std::collections::{BinaryHeap, VecDeque};
3use std::time::{Duration, Instant};
4
5#[derive(Debug, PartialEq, Eq)]
6struct Event<T> {
7    item: T,
8    release_at: Instant,
9}
10
11impl<T: Eq> Ord for Event<T> {
12    fn cmp(&self, other: &Self) -> Ordering {
13        other.release_at.cmp(&self.release_at) // reverse ordering for min-heap
14    }
15}
16
17impl<T: Eq> PartialOrd for Event<T> {
18    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
19        Some(self.cmp(other))
20    }
21}
22
23/// Current state of the debouncing buffer returned from [Get::get()]:
24///
25/// - `Ready(T)` when the event is ready to be delivered after the timeout
26///   (moves data out of the buffer)
27/// - `Wait(Duration)` indicates how much time is left until `Ready`
28/// - `Empty` means the buffer is empty
29#[derive(Debug, PartialEq, Eq)]
30pub enum State<T> {
31    Ready(T),
32    Wait(Duration),
33    Empty,
34}
35
36/// Common interface for getting events out of debouncing buffers.
37pub trait Get: Sized {
38    type Data;
39
40    /// Attemtps to get the next element out of a buffer. If an element is
41    /// [State::Ready] it's removed from the buffer.
42    fn get(&mut self) -> State<Self::Data>;
43
44    /// Returns an iterator over all [State::Ready] elements of the buffer.
45    /// Stops when either the next element is in [State::Wait] or the buffer
46    /// is [State::Empty].
47    fn iter(&mut self) -> BufferIter<Self> {
48        BufferIter(self)
49    }
50}
51
52/// Wraps a mutable reference to a buffer and implements an [Iterator] returning
53/// elements in [State::Ready]. Commonly instantiated by [Get::iter()].
54pub struct BufferIter<'a, B: Get>(&'a mut B);
55
56impl<'a, B: Get> Iterator for BufferIter<'a, B> {
57    type Item = B::Data;
58
59    fn next(&mut self) -> Option<Self::Item> {
60        match self.0.get() {
61            State::Ready(data) => Some(data),
62            _ => None,
63        }
64    }
65}
66/// Debouncing buffer with a common delay for all events. Accepts events via
67/// [EventBuffer::put()] which tracks the time of events and de-duplicates them
68/// against the current buffer content. Subsequent call to [EventBuffer::get
69/// ()] which returns the [State] of the buffer.
70///
71/// Implemented on top of a [VecDeque] and should be slightly more preformant
72/// than [MixedEventBuffer].
73pub struct EventBuffer<T> {
74    delay: Duration,
75    events: VecDeque<Event<T>>,
76}
77
78impl<T: PartialEq> EventBuffer<T> {
79    pub fn new(delay: Duration) -> EventBuffer<T> {
80        EventBuffer {
81            delay,
82            events: VecDeque::new(),
83        }
84    }
85
86    pub fn put(&mut self, item: T) {
87        let time = Instant::now();
88        self.events
89            .retain(|e| e.release_at <= time || e.item != item);
90        self.events.push_back(Event {
91            item,
92            release_at: time + self.delay,
93        });
94    }
95}
96
97impl<T> Get for EventBuffer<T> {
98    type Data = T;
99
100    fn get(&mut self) -> State<T> {
101        let time = Instant::now();
102        match self.events.get(0) {
103            None => State::Empty,
104            Some(e) if e.release_at > time => State::Wait(e.release_at - time),
105            Some(_) => State::Ready(self.events.pop_front().unwrap().item),
106        }
107    }
108}
109
110/// Debouncing buffer with per-event delays. Accepts events via
111/// [MixedEventBuffer::put()] passing a `delay` as an argument. The call
112/// tracks the time of events and de-duplicates them against the current buffer
113/// content. Subsequent call to [MixedEventBuffer::get()] which returns the
114/// [State] of the buffer.
115///
116/// Implemented on top of a [BinaryHeap] and should be slightly less preformant
117/// than [EventBuffer].
118pub struct MixedEventBuffer<T> {
119    events: BinaryHeap<Event<T>>,
120}
121
122impl<T: Eq> MixedEventBuffer<T> {
123    pub fn new() -> MixedEventBuffer<T> {
124        MixedEventBuffer {
125            events: BinaryHeap::new(),
126        }
127    }
128
129    pub fn put(&mut self, item: T, delay: Duration) {
130        let time = Instant::now();
131        self.events
132            .retain(|e| e.release_at <= time || e.item != item);
133        self.events.push(Event {
134            item,
135            release_at: time + delay,
136        });
137    }
138}
139
140impl<T: Eq> Get for MixedEventBuffer<T> {
141    type Data = T;
142
143    fn get(&mut self) -> State<T> {
144        let time = Instant::now();
145        match self.events.peek() {
146            None => State::Empty,
147            Some(e) if e.release_at > time => State::Wait(e.release_at - time),
148            Some(_) => State::Ready(self.events.pop().unwrap().item),
149        }
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156    use std::thread::sleep;
157    use std::time::Duration;
158
159    mod event_buffer {
160        use super::*;
161
162        #[test]
163        fn wait() {
164            let mut debouncer = EventBuffer::new(Duration::from_millis(20));
165            debouncer.put(1);
166            assert!(matches!(debouncer.get(), State::Wait(_)));
167            sleep(Duration::from_millis(10));
168            assert!(matches!(debouncer.get(), State::Wait(_)));
169            sleep(Duration::from_millis(10));
170            assert!(matches!(debouncer.get(), State::Ready(_)));
171        }
172
173        #[test]
174        fn deduplication() {
175            let mut debouncer = EventBuffer::new(Duration::from_millis(20));
176            debouncer.put(1);
177            debouncer.put(2);
178            sleep(Duration::from_millis(10));
179            debouncer.put(1);
180            sleep(Duration::from_millis(20));
181            assert!(debouncer.iter().eq([2, 1]));
182        }
183    }
184
185    mod mixed_event_buffer {
186        use super::*;
187
188        #[test]
189        fn wait() {
190            let mut debouncer = MixedEventBuffer::new();
191            debouncer.put(1, Duration::from_millis(20));
192            assert!(matches!(debouncer.get(), State::Wait(_)));
193            sleep(Duration::from_millis(10));
194            assert!(matches!(debouncer.get(), State::Wait(_)));
195            sleep(Duration::from_millis(10));
196            assert!(matches!(debouncer.get(), State::Ready(_)));
197        }
198
199        #[test]
200        fn deduplication() {
201            let mut debouncer = MixedEventBuffer::new();
202            debouncer.put(1, Duration::from_millis(20));
203            debouncer.put(2, Duration::from_millis(30));
204            sleep(Duration::from_millis(10));
205            debouncer.put(1, Duration::from_millis(10));
206            sleep(Duration::from_millis(20));
207            assert!(debouncer.iter().eq([1, 2]));
208        }
209
210        #[test]
211        fn event_order() {
212            let mut debouncer = MixedEventBuffer::new();
213            debouncer.put(2, Duration::from_millis(20));
214            debouncer.put(1, Duration::from_millis(10));
215            sleep(Duration::from_millis(30));
216            assert!(debouncer.iter().eq([1, 2]));
217        }
218    }
219}