use crate::buffer::{EventBuffer, Get, MixedEventBuffer, State};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::thread::{self, JoinHandle};
use std::time::Duration;
struct DebouncerThread<B> {
mutex: Arc<Mutex<B>>,
thread: JoinHandle<()>,
stopped: Arc<AtomicBool>,
}
impl<B> DebouncerThread<B> {
fn new<F>(buffer: B, mut f: F) -> Self
where
B: Get + Send + 'static,
F: FnMut(B::Data) + Send + 'static,
{
let mutex = Arc::new(Mutex::new(buffer));
let stopped = Arc::new(AtomicBool::new(false));
let thread = thread::spawn({
let mutex = mutex.clone();
let stopped = stopped.clone();
move || {
while !stopped.load(Ordering::Relaxed) {
let state = mutex.lock().unwrap().get();
match state {
State::Empty => thread::park(),
State::Wait(duration) => thread::sleep(duration),
State::Ready(data) => f(data),
}
}
}
});
Self {
mutex,
thread,
stopped,
}
}
fn stop(self) -> JoinHandle<()> {
self.stopped.store(true, Ordering::Relaxed);
self.thread
}
}
pub struct EventDebouncer<T>(DebouncerThread<EventBuffer<T>>);
impl<T: PartialEq> EventDebouncer<T> {
pub fn new<F>(delay: Duration, f: F) -> Self
where
F: FnMut(T) + Send + 'static,
T: Send + 'static,
{
Self(DebouncerThread::new(EventBuffer::new(delay), f))
}
pub fn put(&self, data: T) {
self.0.mutex.lock().unwrap().put(data);
self.0.thread.thread().unpark();
}
pub fn stop(self) -> JoinHandle<()> {
self.0.stop()
}
}
pub struct MixedEventDebouncer<T>(DebouncerThread<MixedEventBuffer<T>>);
impl<T: Eq> MixedEventDebouncer<T> {
pub fn new<F>(f: F) -> Self
where
F: FnMut(T) + Send + 'static,
T: Send + 'static,
{
Self(DebouncerThread::new(MixedEventBuffer::new(), f))
}
pub fn put(&self, data: T, delay: Duration) {
self.0.mutex.lock().unwrap().put(data, delay);
self.0.thread.thread().unpark();
}
pub fn stop(self) -> JoinHandle<()> {
self.0.stop()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::mpsc::channel;
#[test]
fn event_debouncer() {
let (tx, rx) = channel();
let debouncer = EventDebouncer::new(Duration::from_millis(10), move |s| {
tx.send(s).unwrap();
});
debouncer.put(String::from("Test"));
debouncer.put(String::from("Test"));
thread::sleep(Duration::from_millis(20));
assert!(rx.try_iter().eq([String::from("Test")]));
}
#[test]
fn mixed_event_debouncer() {
let (tx, rx) = channel();
let debouncer = MixedEventDebouncer::new(move |s| {
tx.send(s).unwrap();
});
debouncer.put(String::from("Test"), Duration::from_millis(10));
debouncer.put(String::from("Test"), Duration::from_millis(10));
thread::sleep(Duration::from_millis(20));
assert!(rx.try_iter().eq([String::from("Test")]));
}
}