From 3361c3d4da7123cf0e79b8294f0c63c58d85cbd0 Mon Sep 17 00:00:00 2001 From: Aron Heinecke Date: Sun, 14 Mar 2021 02:57:56 +0100 Subject: [PATCH] Add debouncer Signed-off-by: Aron Heinecke --- src/debouncer.rs | 110 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 2 files changed, 112 insertions(+) create mode 100644 src/debouncer.rs diff --git a/src/debouncer.rs b/src/debouncer.rs new file mode 100644 index 00000000..004faf90 --- /dev/null +++ b/src/debouncer.rs @@ -0,0 +1,110 @@ +//! Debouncer & access code +use std::{collections::HashMap, path::PathBuf, sync::{Arc, Mutex, mpsc::{self, Receiver}}, time::{Duration, Instant}}; + +use crate::{Error, Event, EventKind, RecommendedWatcher, Watcher}; + +/// Access to debouncer +#[derive(Default)] +struct Debouncer { + +} + +struct EventData { + event: EventKind, + time: Instant, +} + +impl From for EventData { + fn from(e: EventKind) -> Self { + EventData { + event: e, + time: Instant::now(), + } + } +} + +/// Creates a new debounced watcher +pub fn new_debouncer(timeout: Duration) -> Result<(Receiver>,RecommendedWatcher), Error> { + let events: Arc>> = Arc::new(Mutex::new(HashMap::new())); + + let (tx,rx) = mpsc::channel(); + + let events_c = events.clone(); + std::thread::spawn(move ||{ + loop { + std::thread::sleep(timeout); + let mut data = HashMap::new(); + { + let mut lck = events_c.lock().expect("Can't lock events map"); + + let mut data_back = HashMap::new(); + // TODO: use drain_filter if stabilized + for (k,v) in lck.drain() { + if v.time.elapsed() >= timeout { + data.insert(k,v.event); + } else { + data_back.insert(k,v); + } + } + *lck = data_back; + } + + if tx.send(data).is_err() { + break; + } + } + }); + + let watcher = RecommendedWatcher::new_immediate(move |e: Result| { + let mut lock = events.lock().expect("Can't lock mutex!"); + if let Ok(v) = e { + match &v.kind { + EventKind::Any | EventKind::Other => { + for p in v.paths.into_iter() { + if let Some(existing) = lock.get(&p) { + // TODO: consider EventKind::Any + match existing.event { + EventKind::Any | EventKind::Other => (), + _ => continue, + } + } + lock.insert(p, v.kind.clone().into()); + } + }, + EventKind::Access(_t) => { + for p in v.paths.into_iter() { + if let Some(existing) = lock.get(&p) { + // TODO: consider EventKind::Any + match existing.event { + EventKind::Access(_) | EventKind::Any | EventKind::Other => (), + _ => continue, + } + } + lock.insert(p, v.kind.clone().into()); + } + }, + EventKind::Modify(_t) => { + for p in v.paths.into_iter() { + if let Some(existing) = lock.get(&p) { + // TODO: consider EventKind::Any on invalid configurations + match existing.event { + EventKind::Access(_) | EventKind::Any | EventKind::Other => (), + _ => continue, + } + } + lock.insert(p, v.kind.clone().into()); + } + }, + // ignore previous events, override + EventKind::Create(_) | EventKind::Remove(_) => { + for p in v.paths.into_iter() { + lock.insert(p, v.kind.clone().into()); + } + }, + } + } + })?; + + + Ok((rx,watcher)) +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 4e2fa754..cf90cc62 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,6 +102,7 @@ pub use config::{Config, RecursiveMode}; pub use error::{Error, ErrorKind, Result}; pub use event::{Event, EventKind}; +pub use debouncer::new_debouncer; use std::convert::AsRef; use std::path::Path; @@ -125,6 +126,7 @@ pub mod event; pub mod null; pub mod poll; +mod debouncer; mod config; mod error;