diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 61f92698..0e64afff 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -33,6 +33,10 @@ path = "poll_sysfs.rs" name = "watcher_kind" path = "watcher_kind.rs" +[[example]] +name = "multiple_backends" +path = "multiple_backends.rs" + # specifically in its own sub folder # to prevent cargo audit from complaining #[[example]] diff --git a/examples/multiple_backends.rs b/examples/multiple_backends.rs new file mode 100644 index 00000000..dd1b1969 --- /dev/null +++ b/examples/multiple_backends.rs @@ -0,0 +1,39 @@ +use notify::{RecursiveMode, Result, Watcher, Config}; +use std::path::Path; +fn direct_init() -> Result<()> { + fn event_fn(res: Result) { + match res { + Ok(event) => println!("event: {:?}", event), + Err(e) => println!("watch error: {:?}", e), + } + } + + let mut watcher1 = notify::recommended_watcher(event_fn)?; + // we will just use the same watcher kind again here + let mut watcher2 = notify::recommended_watcher(event_fn)?; + watcher1.watch(Path::new("."), RecursiveMode::Recursive)?; + watcher2.watch(Path::new("."), RecursiveMode::Recursive)?; + Ok(()) +} + +fn fallback_init() -> Result<()> { + fn event_fn(res: Result) { + match res { + Ok(event) => println!("event: {:?}", event), + Err(e) => println!("watch error: {:?}", e), + } + } + + let mut watcher1 = notify::recommended_watcher_fallback(event_fn, Config::default())?.take_boxed(); + // we will just use the same watcher kind again here + let mut watcher2 = notify::recommended_watcher_fallback(event_fn, Config::default())?.take_boxed(); + watcher1.watch(Path::new("."), RecursiveMode::Recursive)?; + watcher2.watch(Path::new("."), RecursiveMode::Recursive)?; + Ok(()) +} + +fn main() -> Result<()> { + direct_init()?; + fallback_init()?; + Ok(()) +} \ No newline at end of file diff --git a/notify/src/lib.rs b/notify/src/lib.rs index c0b32606..4759a04e 100644 --- a/notify/src/lib.rs +++ b/notify/src/lib.rs @@ -371,7 +371,7 @@ pub type RecommendedWatcher = PollWatcher; /// Convenience method for creating the `RecommendedWatcher` for the current platform in /// _immediate_ mode. /// -/// See [`Watcher::new_immediate`](trait.Watcher.html#tymethod.new_immediate). +/// See [`Watcher::new`](trait.Watcher.html#tymethod.new). pub fn recommended_watcher(event_handler: F) -> Result where F: EventHandler, @@ -380,6 +380,58 @@ where RecommendedWatcher::new(event_handler, Config::default()) } +/// Method for creating the `RecommendedWatcher` for the current platform +/// and falling back if the recommended API is not available. +/// +/// Example use case is [#423] where Docker on M1 in qemu does not expose the inotify OS API. +/// +/// See also [`Watcher::new`](trait.Watcher.html#tymethod.new). +pub fn recommended_watcher_fallback(event_handler: F, config: Config) -> Result +where + F: EventHandler + Copy, +{ + // All recommended watchers currently implement `new`, so just call that. + match RecommendedWatcher::new(event_handler, config.clone()) { + Ok(v) => Ok(WatcherFallback::Native(v)), + Err(e) => { + match &e.kind { + #[allow(unused)] + ErrorKind::Io(io_error) => { + #[cfg(target_os = "linux")] + if io_error.raw_os_error() == Some(38) { + return Ok(WatcherFallback::Fallback(PollWatcher::new(event_handler, config)?)); + } + }, + _ => (), + } + return Err(e); + }, + } +} + +/// Wrapper for a fallback initialized watcher. +/// +/// See [`recommended_watcher_fallback`](recommended_watcher_fallback) +#[derive(Debug)] +pub enum WatcherFallback { + /// Native watcher, no error occured + Native(RecommendedWatcher), + /// Fallback watcher, known platform issue occured + /// + /// For example the Docker with Linux on MacOS M1 bug + Fallback(PollWatcher) +} + +impl WatcherFallback { + /// Returns the watcher inside + pub fn take_boxed(self) -> Box{ + match self { + WatcherFallback::Native(v) => Box::new(v), + WatcherFallback::Fallback(v) => Box::new(v), + } + } +} + #[cfg(test)] mod tests { use super::*;