use std::{
pin::Pin,
sync::{mpsc, Arc, Mutex},
time::Duration,
};
pub fn debounce<F, T>(closure: F, delay: Duration) -> Debounce<T>
where
F: Fn(T) + Send + Sync + 'static,
T: Send + Sync + 'static,
{
let (sender, receiver) = mpsc::channel();
let sender = Arc::new(Mutex::new(sender));
let debounce_config = Arc::new(Mutex::new(DebounceConfig {
closure: Box::pin(closure),
delay,
}));
let dup_debounce_config = debounce_config.clone();
Debounce {
sender: Some(sender),
thread: Some(std::thread::spawn(move || {
let debounce_config = dup_debounce_config;
let mut current_param = None; loop {
if current_param.is_none() {
let message = receiver.recv();
match message {
Ok(param) => current_param = param,
Err(_) => {
break;
}
}
} else {
let message = receiver.recv_timeout(debounce_config.lock().unwrap().delay);
match message {
Ok(param) => current_param = param,
Err(err) => match err {
mpsc::RecvTimeoutError::Timeout => {
if let Some(param) = current_param.take() {
(*debounce_config.lock().unwrap().closure)(param);
}
}
mpsc::RecvTimeoutError::Disconnected => {
break;
}
},
}
}
}
})),
debounce_config,
}
}
struct DebounceConfig<T> {
closure: Pin<Box<dyn Fn(T) + Send + Sync + 'static>>,
delay: Duration,
}
impl<T> Drop for DebounceConfig<T> {
fn drop(&mut self) {
log::trace!("drop DebounceConfig {:?}", format!("{:p}", self));
}
}
#[allow(dead_code)]
pub struct Debounce<T> {
sender: Option<Arc<Mutex<mpsc::Sender<Option<T>>>>>,
thread: Option<std::thread::JoinHandle<()>>,
debounce_config: Arc<Mutex<DebounceConfig<T>>>,
}
impl<T> Debounce<T> {
pub fn call(&self, param: T) {
self.sender
.as_ref()
.unwrap()
.lock()
.unwrap()
.send(Some(param))
.unwrap();
}
pub fn terminate(&self) {
self.sender
.as_ref()
.unwrap()
.lock()
.unwrap()
.send(None)
.unwrap();
}
}
impl<T> Drop for Debounce<T> {
fn drop(&mut self) {
self.terminate();
log::trace!("drop Debounce {:?}", format!("{:p}", self));
}
}