use std::{
path::{Path, PathBuf},
sync::mpsc::{self, Receiver, TryRecvError},
};
use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher};
use crate::error::{Result, SyncwebError};
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct FsEvent {
pub event: Event,
pub paths: Vec<PathBuf>,
}
pub struct FsWatcher {
watcher: RecommendedWatcher,
receiver: Receiver<Result<FsEvent>>,
}
impl std::fmt::Debug for FsWatcher {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.debug_struct("FsWatcher").finish_non_exhaustive()
}
}
impl FsWatcher {
pub fn new(path: impl AsRef<Path>) -> Result<Self> {
let (sender, receiver) = mpsc::channel();
let watcher = notify::recommended_watcher(move |event_result: notify::Result<Event>| {
let result = event_result
.map(|event| FsEvent {
event: event.clone(),
paths: event.paths,
})
.map_err(|error| SyncwebError::operation("filesystem watcher event failed", error));
let _ = sender.send(result);
})
.map_err(|error| SyncwebError::operation("failed to create filesystem watcher", error))?;
let mut this = Self { watcher, receiver };
this.watcher
.watch(path.as_ref(), RecursiveMode::Recursive)
.map_err(|error| SyncwebError::operation("failed to watch filesystem path", error))?;
Ok(this)
}
pub fn recv(&self) -> Result<FsEvent> {
self.receiver
.recv()
.map_err(|error| SyncwebError::operation("filesystem watcher stopped", error))?
}
pub fn try_recv(&self) -> Result<Option<FsEvent>> {
match self.receiver.try_recv() {
Ok(event) => event.map(Some),
Err(TryRecvError::Empty) => Ok(None),
Err(TryRecvError::Disconnected) => Err(SyncwebError::operation(
"filesystem watcher stopped",
"event channel disconnected",
)),
}
}
pub fn unwatch(&mut self, path: impl AsRef<Path>) -> Result<()> {
self.watcher
.unwatch(path.as_ref())
.map_err(|error| SyncwebError::operation("failed to stop filesystem watcher", error))
}
}