use std::collections::HashSet;
use std::path::{Path, PathBuf};
use std::sync::mpsc::{Receiver, TryRecvError, channel};
use notify::{EventKind, RecommendedWatcher, RecursiveMode, Watcher as NotifyWatcher};
pub struct Watcher {
inner: RecommendedWatcher,
events: Receiver<notify::Result<notify::Event>>,
watched: HashSet<PathBuf>,
}
impl std::fmt::Debug for Watcher {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Watcher")
.field("watched", &self.watched.len())
.finish()
}
}
impl Watcher {
#[must_use]
pub fn new() -> Option<Self> {
let (sender, events) = channel();
let inner = notify::recommended_watcher(sender).ok()?;
Some(Self {
inner,
events,
watched: HashSet::new(),
})
}
pub fn watch(&mut self, path: &Path) {
let Some(parent) = path.parent().map(Path::to_path_buf) else {
return;
};
if !self.watched.insert(parent.clone()) {
return;
}
if self
.inner
.watch(&parent, RecursiveMode::NonRecursive)
.is_err()
{
self.watched.remove(&parent);
}
}
pub fn drain(&mut self) -> Vec<PathBuf> {
let mut changed: Vec<PathBuf> = Vec::new();
loop {
match self.events.try_recv() {
Ok(Ok(event)) => {
if !matches!(
event.kind,
EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_)
) {
continue;
}
for path in event.paths {
if !changed.contains(&path) {
changed.push(path);
}
}
}
Ok(Err(_)) => {}
Err(TryRecvError::Empty | TryRecvError::Disconnected) => break,
}
}
changed
}
}