use crate::{Error, Result, SPath};
use notify::{self, RecommendedWatcher, RecursiveMode, Watcher};
use notify_debouncer_full::{new_debouncer, DebounceEventHandler, DebounceEventResult, Debouncer, FileIdMap};
use std::path::Path;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::time::Duration;
pub use notify_debouncer_full::DebouncedEvent;
use std::collections::HashSet;
const WATCH_DEBOUNCE_MS: u64 = 200;
#[derive(Debug)]
pub struct SEvent {
pub spath: SPath,
pub skind: SEventKind,
}
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
pub enum SEventKind {
Create,
Modify,
Remove,
Other,
}
impl From<notify::EventKind> for SEventKind {
fn from(val: notify::EventKind) -> Self {
match val {
notify::EventKind::Any => SEventKind::Other,
notify::EventKind::Access(_) => SEventKind::Other,
notify::EventKind::Create(_) => SEventKind::Create,
notify::EventKind::Modify(_) => SEventKind::Modify,
notify::EventKind::Remove(_) => SEventKind::Remove,
notify::EventKind::Other => SEventKind::Other,
}
}
}
#[allow(unused)]
pub struct SWatcher {
pub rx: Receiver<Vec<SEvent>>,
notify_full_debouncer: Debouncer<RecommendedWatcher, FileIdMap>,
}
pub fn watch(path: impl AsRef<Path>) -> Result<SWatcher> {
let (tx, rx) = channel();
let handler = EventHandler { tx };
let mut debouncer = new_debouncer(Duration::from_millis(WATCH_DEBOUNCE_MS), None, handler).unwrap();
let watcher = debouncer.watcher();
let path = path.as_ref();
if !path.exists() {
return Err(Error::CantWatchPathNotFound(path.to_string_lossy().to_string()));
}
watcher
.watch(path, RecursiveMode::Recursive)
.map_err(|err| Error::FailToWatch {
path: path.to_string_lossy().to_string(),
cause: err.to_string(),
})?;
let swatcher = SWatcher {
rx,
notify_full_debouncer: debouncer,
};
Ok(swatcher)
}
struct EventHandler {
tx: Sender<Vec<SEvent>>,
}
impl DebounceEventHandler for EventHandler {
fn handle_event(&mut self, result: DebounceEventResult) {
match result {
Ok(events) => {
let sevents = build_sevents(events);
if !sevents.is_empty() {
let _ = self.tx.send(sevents);
}
}
Err(err) => println!("simple-fs - handle_event error {err:?}"), }
}
}
#[derive(Hash, Eq, PartialEq)]
struct SEventKey {
spath_string: String,
skind: SEventKind,
}
fn build_sevents(events: Vec<DebouncedEvent>) -> Vec<SEvent> {
let mut sevents_set: HashSet<SEventKey> = HashSet::new();
let mut sevents = Vec::new();
for devent in events {
let event = devent.event;
let skind = SEventKind::from(event.kind);
for path in event.paths {
if let Some(spath) = SPath::from_path_buf_ok(path) {
let key = SEventKey {
spath_string: spath.to_string(),
skind: skind.clone(),
};
if !sevents_set.contains(&key) {
sevents.push(SEvent {
spath,
skind: skind.clone(),
});
sevents_set.insert(key);
}
}
}
}
sevents
}