notify_win/lib.rs
1//! Windows file system notification library
2//!
3//! Fork of [notify](https://github.com/notify-rs/notify)
4//!
5//! # Installation
6//!
7//! ```toml
8//! [dependencies]
9//! notify-win = "0.1.0"
10//! ```
11//!
12//! ## Features
13//!
14//! List of compilation features, see below for details
15//!
16//! - `serde` for serialization of events
17//! - `serialization-compat-6` restores the serialization behavior of notify 6, off by default
18//!
19//! ### Serde
20//!
21//! Events are serializable via [serde](https://serde.rs) if the `serde` feature is enabled:
22//!
23//! ```toml
24//! notify-win = { version = "0.1.0", features = ["serde"] }
25//! ```
26//!
27//! # Known Problems
28//!
29//! ### Network filesystems
30//!
31//! Network mounted filesystems like NFS may not emit any events for notify to listen to.
32//! This applies especially to WSL programs watching windows paths.
33//!
34//! A workaround is the [`PollWatcher`] backend.
35//!
36//! ### Editor Behaviour
37//!
38//! If you rely on precise events (Write/Delete/Create..), you will notice that the actual events
39//! can differ a lot between file editors. Some truncate the file on save, some create a new one and replace the old one.
40//!
41//! ### Parent folder deletion
42//!
43//! If you want to receive an event for a deletion of folder `b` for the path `/a/b/..`, you will have to watch its parent `/a`.
44//!
45//! ### Pseudo Filesystems like /proc, /sys
46//!
47//! Some filesystems like `/proc` and `/sys` on *nix do not emit change events or use correct file change dates.
48//! To circumvent that problem you can use the [`PollWatcher`] with the `compare_contents` option.
49//!
50//! Note that the [`PollWatcher`] is not restricted by this limitation, so it may be an alternative if your users can't increase the limit.
51//!
52//! ### Watching large directories
53//!
54//! When watching a very large amount of files, notify may fail to receive all events.
55//!
56//! ```rust
57//! use notify_win::{Event, RecursiveMode, Result, Watcher};
58//! use std::sync::mpsc;
59//!
60//! fn main() -> Result<()> {
61//! let (tx, rx) = mpsc::channel::<Result<Event>>();
62//!
63//! // Use recommended_watcher() to automatically select the best implementation
64//! // for your platform. The `EventHandler` passed to this constructor can be a
65//! // closure, a `std::sync::mpsc::Sender`, a `crossbeam_channel::Sender`, or
66//! // another type the trait is implemented for.
67//! let mut watcher = notify_win::recommended_watcher(tx)?;
68//!
69//! // Add a path to be watched. All files and directories at that path and
70//! // below will be monitored for changes.
71//! # { // "." doesn't exist on BSD for some reason in CI
72//! watcher.watch(Path::new("."), RecursiveMode::Recursive)?;
73//! # }
74//! # #[cfg(any())]
75//! # { // don't run this in doctests, it blocks forever
76//! // Block forever, printing out events as they come in
77//! for res in rx {
78//! match res {
79//! Ok(event) => println!("event: {:?}", event),
80//! Err(e) => println!("watch error: {:?}", e),
81//! }
82//! }
83//! # }
84//!
85//! Ok(())
86//! }
87//! ```
88//!
89//! ## With different configurations
90//!
91//! It is possible to create several watchers with different configurations or implementations that
92//! all call the same event function. This can accommodate advanced behaviour or work around limits.
93//!
94//! ```rust
95//! # use notify_win::{RecursiveMode, Result, Watcher};
96//! # use std::path::Path;
97//! #
98//! # fn main() -> Result<()> {
99//! fn event_fn(res: Result<notify_win::Event>) {
100//! match res {
101//! Ok(event) => println!("event: {:?}", event),
102//! Err(e) => println!("watch error: {:?}", e),
103//! }
104//! }
105//!
106//! let mut watcher1 = notify_win::recommended_watcher(event_fn)?;
107//! // we will just use the same watcher kind again here
108//! let mut watcher2 = notify_win::recommended_watcher(event_fn)?;
109//! # { // "." doesn't exist on BSD for some reason in CI
110//! # watcher1.watch(Path::new("."), RecursiveMode::Recursive)?;
111//! # watcher2.watch(Path::new("."), RecursiveMode::Recursive)?;
112//! # }
113//! // dropping the watcher1/2 here (no loop etc) will end the program
114//! #
115//! # Ok(())
116//! # }
117//! ```
118
119#![deny(missing_docs)]
120
121pub use config::{Config, RecursiveMode};
122pub use error::{Error, ErrorKind, Result};
123pub use notify_win_types::event::{self, Event, EventKind};
124use std::path::Path;
125
126pub(crate) type Receiver<T> = std::sync::mpsc::Receiver<T>;
127pub(crate) type Sender<T> = std::sync::mpsc::Sender<T>;
128pub(crate) type BoundSender<T> = std::sync::mpsc::SyncSender<T>;
129
130mod config;
131mod error;
132
133pub mod null;
134pub mod poll;
135pub mod windows;
136
137#[inline]
138pub(crate) fn unbounded<T>() -> (Sender<T>, Receiver<T>) {
139 std::sync::mpsc::channel()
140}
141
142#[inline]
143pub(crate) fn bounded<T>(cap: usize) -> (BoundSender<T>, Receiver<T>) {
144 std::sync::mpsc::sync_channel(cap)
145}
146
147pub use null::NullWatcher;
148pub use poll::PollWatcher;
149pub use windows::ReadDirectoryChangesWatcher;
150
151/// The set of requirements for watcher event handling functions.
152///
153/// # Example implementation
154///
155/// ```no_run
156/// use notify_win::{Event, Result, EventHandler};
157///
158/// /// Prints received events
159/// struct EventPrinter;
160///
161/// impl EventHandler for EventPrinter {
162/// fn handle_event(&mut self, event: Result<Event>) {
163/// if let Ok(event) = event {
164/// println!("Event: {:?}", event);
165/// }
166/// }
167/// }
168/// ```
169pub trait EventHandler: Send + 'static {
170 /// Handles an event.
171 fn handle_event(&mut self, event: Result<Event>);
172}
173
174impl<F> EventHandler for F
175where
176 F: FnMut(Result<Event>) + Send + 'static,
177{
178 fn handle_event(&mut self, event: Result<Event>) {
179 (self)(event);
180 }
181}
182
183#[cfg(feature = "crossbeam-channel")]
184impl EventHandler for crossbeam_channel::Sender<Result<Event>> {
185 fn handle_event(&mut self, event: Result<Event>) {
186 let _ = self.send(event);
187 }
188}
189
190impl EventHandler for std::sync::mpsc::Sender<Result<Event>> {
191 fn handle_event(&mut self, event: Result<Event>) {
192 let _ = self.send(event);
193 }
194}
195
196/// Watcher kind enumeration
197#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
198#[non_exhaustive]
199pub enum WatcherKind {
200 /// Polling based backend (fallback)
201 PollWatcher,
202 /// Windows backend
203 ReadDirectoryChangesWatcher,
204 /// Fake watcher for testing
205 NullWatcher,
206}
207
208/// Type that can deliver file activity notifications
209///
210/// `Watcher` is implemented per platform using the best implementation available on that platform.
211/// In addition to such event driven implementations, a polling implementation is also provided
212/// that should work on any platform.
213pub trait Watcher {
214 /// Create a new watcher with an initial Config.
215 fn new<F: EventHandler>(event_handler: F, config: config::Config) -> Result<Self>
216 where
217 Self: Sized;
218 /// Begin watching a new path.
219 ///
220 /// If the `path` is a directory, `recursive_mode` will be evaluated. If `recursive_mode` is
221 /// `RecursiveMode::Recursive` events will be delivered for all files in that tree. Otherwise
222 /// only the directory and its immediate children will be watched.
223 ///
224 /// If the `path` is a file, `recursive_mode` will be ignored and events will be delivered only
225 /// for the file.
226 ///
227 /// On some platforms, if the `path` is renamed or removed while being watched, behaviour may
228 /// be unexpected. See discussions in [#165] and [#166]. If less surprising behaviour is wanted
229 /// one may non-recursively watch the _parent_ directory as well and manage related events.
230 fn watch(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()>;
231
232 /// Stop watching a path.
233 ///
234 /// # Errors
235 ///
236 /// Returns an error in the case that `path` has not been watched or if removing the watch
237 /// fails.
238 fn unwatch(&mut self, path: &Path) -> Result<()>;
239
240 /// Configure the watcher at runtime.
241 ///
242 /// See the [`Config`](config/struct.Config.html) struct for all configuration options.
243 ///
244 /// # Returns
245 ///
246 /// - `Ok(true)` on success.
247 /// - `Ok(false)` if the watcher does not support or implement the option.
248 /// - `Err(notify_win::Error)` on failure.
249 fn configure(&mut self, _option: Config) -> Result<bool> {
250 Ok(false)
251 }
252
253 /// Returns the watcher kind, allowing to perform backend-specific tasks
254 fn kind() -> WatcherKind
255 where
256 Self: Sized;
257}
258
259/// The recommended [`Watcher`] implementation for the current platform
260pub type RecommendedWatcher = ReadDirectoryChangesWatcher;
261
262/// Convenience method for creating the [`RecommendedWatcher`] for the current platform.
263pub fn recommended_watcher<F>(event_handler: F) -> Result<RecommendedWatcher>
264where
265 F: EventHandler,
266{
267 // All recommended watchers currently implement `new`, so just call that.
268 RecommendedWatcher::new(event_handler, Config::default())
269}
270
271#[cfg(test)]
272mod tests {
273 use std::{fs, time::Duration};
274
275 use tempfile::tempdir;
276
277 use super::*;
278
279 #[test]
280 fn test_object_safe() {
281 let _watcher: &dyn Watcher = &NullWatcher;
282 }
283
284 #[test]
285 fn test_debug_impl() {
286 macro_rules! assert_debug_impl {
287 ($t:ty) => {{
288 trait NeedsDebug: std::fmt::Debug {}
289 impl NeedsDebug for $t {}
290 }};
291 }
292
293 assert_debug_impl!(Config);
294 assert_debug_impl!(Error);
295 assert_debug_impl!(ErrorKind);
296 assert_debug_impl!(NullWatcher);
297 assert_debug_impl!(PollWatcher);
298 assert_debug_impl!(RecommendedWatcher);
299 assert_debug_impl!(RecursiveMode);
300 assert_debug_impl!(WatcherKind);
301 }
302
303 #[test]
304 fn integration() -> std::result::Result<(), Box<dyn std::error::Error>> {
305 let dir = tempdir()?;
306
307 let (tx, rx) = std::sync::mpsc::channel();
308
309 let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
310
311 watcher.watch(dir.path(), RecursiveMode::Recursive)?;
312
313 let file_path = dir.path().join("file.txt");
314 fs::write(&file_path, b"Lorem ipsum")?;
315
316 let event = rx
317 .recv_timeout(Duration::from_secs(10))
318 .expect("no events received")
319 .expect("received an error");
320
321 assert_eq!(event.paths, vec![file_path]);
322
323 Ok(())
324 }
325}