File Watching
Watch a directory for changes
notify reports filesystem changes through one API. Under it sits a native
backend. That is inotify on Linux, FSEvents on macOS, kqueue on the BSDs and
ReadDirectoryChangesW on Windows. The watcher runs that backend on its own
thread. It hands events back over a std::sync::mpsc channel. That is the
standard library’s multi-producer, single-consumer queue. recommended_watcher
takes the sending half and forwards each change to it as a
Result<Event, Error>. Your thread keeps the receiving half and
reads events by iterating it.
Passing RecursiveMode::Recursive to watch follows the entire subtree.
Files created in nested directories are reported too. Every Event carries an
EventKind and the affected paths. Matching on the kind keeps creations, data
writes and removals. It discards metadata-only noise. The receiver blocks until
the Watcher is dropped. A real tool keeps this loop running for its lifetime.
Run it and edit files under the watched directory to watch events stream in.
use notify::event::{EventKind, ModifyKind};
use notify::{RecursiveMode, Watcher};
use std::path::PathBuf;
use std::sync::mpsc;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let path = std::env::args()
.nth(1)
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from("."));
let (tx, rx) = mpsc::channel();
let mut watcher = notify::recommended_watcher(tx)?;
watcher.watch(&path, RecursiveMode::Recursive)?;
println!("watching {}. Press Ctrl-C to stop.", path.display());
for event in rx {
let event = event?;
match event.kind {
EventKind::Create(_) => println!("created: {:?}", event.paths),
EventKind::Modify(ModifyKind::Data(_)) => {
println!("modified: {:?}", event.paths)
}
EventKind::Remove(_) => println!("removed: {:?}", event.paths),
_ => {}
}
}
Ok(())
}
Debounce a burst of file events
A single logical change often surfaces as several raw events. Editors save by writing a temporary file and renaming it. Some backends emit separate create, modify and metadata notifications for one write. Acting on every raw event makes a watcher rebuild or reload far more often than the user intended.
notify-debouncer-full wraps a notify watcher and coalesces events that
land within a time window. new_debouncer takes that window, an optional tick
rate and a channel. Once a burst settles it delivers one deduplicated
Vec<DebouncedEvent> instead of a flood. It also resolves renames and
drops redundant events, so each path appears once per quiet period.
use notify::RecursiveMode;
use notify_debouncer_full::new_debouncer;
use std::path::PathBuf;
use std::sync::mpsc;
use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let path = std::env::args()
.nth(1)
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from("."));
let (tx, rx) = mpsc::channel();
let mut debouncer = new_debouncer(Duration::from_secs(1), None, tx)?;
debouncer.watch(&path, RecursiveMode::Recursive)?;
println!("watching {}. Press Ctrl-C to stop.", path.display());
for result in rx {
match result {
Ok(events) => {
for event in events {
println!("{:?}: {:?}", event.kind, event.paths);
}
}
Err(errors) => {
for error in errors {
eprintln!("watch error: {error:?}");
}
}
}
}
Ok(())
}