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}