A filesystem watcher built on notify that adds support for watching paths that don't yet exist, interest-based event filtering, and automatic cleanup via RAII handles.
- Watch non-existent paths: Subscribe to paths before they exist. When the path is created, you'll receive events.
- Interest filtering: Specify exactly which event types you care about with fine-grained or coarse-grained filters.
- RAII handles: Watches automatically stop when the
Watchedhandle is dropped. - Polling fallback: Configurable polling covers edge cases that native filesystem notifications miss.
- Batched async delivery: Events are batched and delivered through an async
EventHandlertrait.
use extended_notify::{ArcPath, EventBatch, EventHandler, Interest, Watcher};
use anyhow::Result;
use enumflags2::make_bitflags;
struct MyHandler;
impl EventHandler for MyHandler {
async fn handle_event(&mut self, batch: EventBatch) -> Result<()> {
for (id, event) in batch.iter() {
println!("{:?}: {:?}", id, event);
}
Ok(())
}
}
#[tokio::main]
async fn main() -> Result<()> {
let watcher = WatcherConfigBuilder::default()
.event_handler(MyHandler)
.build()?
.start()?;
// Watch a path that may or may not exist
let interest = make_bitflags!(Interest::{Create | Delete | Modify});
let handle = watcher.add(ArcPath::from("/tmp/watched-file"), interest)?;
// ... do work ...
// Watch stops automatically when handle is dropped
drop(handle);
Ok(())
}Interests are hierarchical. Subscribing to a parent interest matches all child events:
DeletematchesDeleteFile,DeleteFolder,DeleteOtherModifymatches all modification types (data, metadata, rename)Anymatches everything
Special interests:
Established: Synthetic event fired when a watch is first set upAny: Matches all event types
When you watch a path:
- If the path exists, it's watched directly via notify
- If not, the nearest existing ancestor is watched, and polling checks for the path's creation
When a watched path is deleted or renamed away, the watcher automatically transitions back to watching the ancestor and polling. Synthetic Create and Delete events are generated for these transitions if you've subscribed to them.
// Adjust polling interval (default: 1s, minimum: 100ms)
watcher.set_poll_interval(Duration::from_secs(5))?;
// Adjust poll batch size (default: 100, 0 disables polling)
watcher.set_poll_batch(50)?;MIT