1 //! Cross-platform file system notification library
2 //!
3 //! Source is on GitHub: https://github.com/notify-rs/notify
4 //!
5 //! # Installation
6 //!
7 //! ```toml
8 //! [dependencies]
9 //! notify = "4.0.17"
10 //! ```
11 //!
12 //! # Examples
13 //!
14 //! Notify provides two APIs. The default API _debounces_ events (if the backend reports two
15 //! similar events in close succession, Notify will only report one). The raw API emits file
16 //! changes as soon as they happen. For more details, see
17 //! [`Watcher::new_raw`](trait.Watcher.html#tymethod.new_raw) and
18 //! [`Watcher::new`](trait.Watcher.html#tymethod.new).
19 //!
20 //! ## Default (debounced) API
21 //!
22 //! ```no_run
23 //! extern crate notify;
24 //!
25 //! use notify::{Watcher, RecursiveMode, watcher};
26 //! use std::sync::mpsc::channel;
27 //! use std::time::Duration;
28 //!
29 //! fn main() {
30 //!     // Create a channel to receive the events.
31 //!     let (tx, rx) = channel();
32 //!
33 //!     // Create a watcher object, delivering debounced events.
34 //!     // The notification back-end is selected based on the platform.
35 //!     let mut watcher = watcher(tx, Duration::from_secs(10)).unwrap();
36 //!
37 //!     // Add a path to be watched. All files and directories at that path and
38 //!     // below will be monitored for changes.
39 //!     watcher.watch("/home/test/notify", RecursiveMode::Recursive).unwrap();
40 //!
41 //!     loop {
42 //!         match rx.recv() {
43 //!            Ok(event) => println!("{:?}", event),
44 //!            Err(e) => println!("watch error: {:?}", e),
45 //!         }
46 //!     }
47 //! }
48 //! ```
49 //!
50 //! Using the default API is easy, all possible events are described in the
51 //! [`DebouncedEvent`](enum.DebouncedEvent.html) documentation. But in order to understand the
52 //! subtleties of the event delivery, you should read the [`op`](op/index.html) documentation as
53 //! well.
54 //!
55 //! ## Raw API
56 //!
57 //! ```no_run
58 //! extern crate notify;
59 //!
60 //! use notify::{Watcher, RecursiveMode, RawEvent, raw_watcher};
61 //! use std::sync::mpsc::channel;
62 //!
63 //! fn main() {
64 //!     // Create a channel to receive the events.
65 //!     let (tx, rx) = channel();
66 //!
67 //!     // Create a watcher object, delivering raw events.
68 //!     // The notification back-end is selected based on the platform.
69 //!     let mut watcher = raw_watcher(tx).unwrap();
70 //!
71 //!     // Add a path to be watched. All files and directories at that path and
72 //!     // below will be monitored for changes.
73 //!     watcher.watch("/home/test/notify", RecursiveMode::Recursive).unwrap();
74 //!
75 //!     loop {
76 //!         match rx.recv() {
77 //!            Ok(RawEvent{path: Some(path), op: Ok(op), cookie}) => {
78 //!                println!("{:?} {:?} ({:?})", op, path, cookie)
79 //!            },
80 //!            Ok(event) => println!("broken event: {:?}", event),
81 //!            Err(e) => println!("watch error: {:?}", e),
82 //!         }
83 //!     }
84 //! }
85 //! ```
86 //!
87 //! The event structure is described in the [`RawEvent`](struct.RawEvent.html) documentation,
88 //! all possible operations delivered in an event are described in the [`op`](op/index.html)
89 //! documentation.
90 
91 #![deny(missing_docs)]
92 
93 #[macro_use]
94 extern crate bitflags;
95 extern crate filetime;
96 #[cfg(target_os = "macos")]
97 extern crate fsevent_sys;
98 extern crate libc;
99 #[cfg(target_os = "linux")]
100 extern crate mio;
101 #[cfg(target_os = "linux")]
102 extern crate mio_extras;
103 #[cfg(target_os = "windows")]
104 extern crate winapi;
105 
106 pub use self::op::Op;
107 use std::convert::AsRef;
108 use std::error::Error as StdError;
109 use std::fmt;
110 use std::io;
111 use std::path::{Path, PathBuf};
112 use std::result::Result as StdResult;
113 use std::sync::mpsc::Sender;
114 use std::time::Duration;
115 
116 #[cfg(target_os = "macos")]
117 pub use self::fsevent::FsEventWatcher;
118 #[cfg(target_os = "linux")]
119 pub use self::inotify::INotifyWatcher;
120 pub use self::null::NullWatcher;
121 pub use self::poll::PollWatcher;
122 #[cfg(target_os = "windows")]
123 pub use self::windows::ReadDirectoryChangesWatcher;
124 
125 #[cfg(target_os = "macos")]
126 pub mod fsevent;
127 #[cfg(target_os = "linux")]
128 pub mod inotify;
129 #[cfg(target_os = "windows")]
130 pub mod windows;
131 
132 pub mod null;
133 pub mod poll;
134 
135 mod debounce;
136 
137 /// Contains the `Op` type which describes the actions for an event.
138 ///
139 /// `notify` aims to provide unified behavior across platforms. This however is not always possible
140 /// due to the underlying technology of the various operating systems. So there are some issues
141 /// `notify`-API users will have to take care of themselves, depending on their needs.
142 ///
143 ///
144 /// # Chmod
145 ///
146 /// __Linux, macOS__
147 ///
148 /// On Linux and macOS the `CHMOD` event is emitted whenever attributes or extended attributes
149 /// change.
150 ///
151 /// __Windows__
152 ///
153 /// On Windows a `WRITE` event is emitted when attributes change. This makes it impossible to
154 /// distinguish between writes to a file or its meta data.
155 ///
156 ///
157 /// # Close-Write
158 ///
159 /// A `CLOSE_WRITE` event is emitted whenever a file that was opened for writing has been closed.
160 ///
161 /// __This event is only available on Linux__.
162 ///
163 ///
164 /// # Create
165 ///
166 /// A `CREATE` event is emitted whenever a new file or directory is created.
167 ///
168 /// Upon receiving a `Create` event for a directory, it is necessary to scan the newly created
169 /// directory for contents. The directory can contain files or directories if those contents were
170 /// created before the directory could be watched, or if the directory was moved into the watched
171 /// directory.
172 ///
173 /// # Remove
174 ///
175 /// ## Remove file or directory within a watched directory
176 ///
177 /// A `REMOVE` event is emitted whenever a file or directory is removed.
178 ///
179 /// ## Remove watched file or directory itself
180 ///
181 /// With the exception of Windows a `REMOVE` event is emitted whenever the watched file or
182 /// directory itself is removed. The behavior after the remove differs between platforms though.
183 ///
184 /// __Linux__
185 ///
186 /// When a watched file or directory is removed, its watch gets destroyed and no new events will be
187 /// sent.
188 ///
189 /// __Windows__
190 ///
191 /// If a watched directory is removed, an empty event is emitted.
192 ///
193 /// When watching a single file on Windows, the file path will continue to be watched until either
194 /// the watch is removed by the API user or the parent directory gets removed.
195 ///
196 /// When watching a directory on Windows, the watch will get destroyed and no new events will be
197 /// sent.
198 ///
199 /// __macOS__
200 ///
201 /// While Linux and Windows monitor "inodes", macOS monitors "paths". So a watch stays active even
202 /// after the watched file or directory has been removed and it will emit events in case a new file
203 /// or directory is created in its place.
204 ///
205 ///
206 /// # Rename
207 ///
208 /// A `RENAME` event is emitted whenever a file or directory has been renamed or moved to a
209 /// different directory.
210 ///
211 /// ## Rename file or directory within a watched directory
212 ///
213 /// __Linux, Windows__
214 ///
215 /// A rename with both the source and the destination path inside a watched directory produces two
216 /// `RENAME` events. The first event contains the source path, the second contains the destination
217 /// path. Both events share the same cookie.
218 ///
219 /// A rename that originates inside of a watched directory but ends outside of a watched directory
220 /// produces a `DELETE` event.
221 ///
222 /// A rename that originates outside of a watched directory and ends inside of a watched directory
223 /// produces a `CREATE` event.
224 ///
225 /// __macOS__
226 ///
227 /// A `RENAME` event is produced whenever a file or directory is moved. This includes moves within
228 /// the watched directory as well as moves into or out of the watched directory. It is up to the
229 /// API user to determine what exactly happened. Usually when a move within a watched directory
230 /// occurs, the cookie is set for both connected events. This can however fail eg. if a file gets
231 /// renamed multiple times without a delay (test `fsevents_rename_rename_file_0`). So in some cases
232 /// rename cannot be caught properly but would be interpreted as a sequence of events where a file
233 /// or directory is moved out of the watched directory and a different file or directory is moved
234 /// in.
235 ///
236 /// ## Rename watched file or directory itself
237 ///
238 /// With the exception of Windows a `RENAME` event is emitted whenever the watched file or
239 /// directory itself is renamed. The behavior after the rename differs between platforms though.
240 /// Depending on the platform either the moved file or directory will continue to be watched or the
241 /// old path. If the moved file or directory will continue to be watched, the paths of emitted
242 /// events will still be prefixed with the old path though.
243 ///
244 /// __Linux__
245 ///
246 /// Linux will continue to watch the moved file or directory. Events will contain paths prefixed
247 /// with the old path.
248 ///
249 /// __Windows__
250 ///
251 /// Currently there is no event emitted when a watched directory is renamed. But the directory will
252 /// continue to be watched and events will contain paths prefixed with the old path.
253 ///
254 /// When renaming a watched file, a `RENAME` event is emitted but the old path will continue to be
255 /// watched.
256 ///
257 /// __macOS__
258 ///
259 /// macOS will continue to watch the (now non-existing) path.
260 ///
261 /// ## Rename parent directory of watched file or directory
262 ///
263 /// Currently no event will be emitted when any parent directory of the watched file or directory
264 /// is renamed. Depending on the platform either the moved file or directory will continue to be
265 /// watched or the old path. If the moved file or directory will continue to be watched, the paths
266 /// of emitted events will still be prefixed with the old path though.
267 ///
268 /// __Linux, Windows__
269 ///
270 /// Linux and Windows will continue to watch the moved file or directory. Events will contain paths
271 /// prefixed with the old path.
272 ///
273 /// __macOS__
274 ///
275 /// macOS will continue to watch the (now non-existing) path.
276 ///
277 ///
278 /// # Rescan
279 ///
280 /// A `RESCAN` event indicates that an error occurred and the watched directories need to be
281 /// rescanned. This can happen if the internal event queue has overflown and some events were
282 /// dropped. Or with FSEvents if events were coalesced hierarchically.
283 ///
284 /// __Windows__
285 ///
286 /// At the moment `RESCAN` events aren't emitted on Windows.
287 ///
288 /// __Queue size__
289 ///
290 /// Linux: `/proc/sys/fs/inotify/max_queued_events`
291 ///
292 /// Windows: 16384 Bytes. The actual amount of events that fit into the queue depends on the length
293 /// of the paths.
294 ///
295 ///
296 /// # Write
297 ///
298 /// A `WRITE` event is emitted whenever a file has been written to.
299 ///
300 /// __Windows__
301 ///
302 /// On Windows a `WRITE` event is emitted when attributes change.
303 #[allow(missing_docs)]
304 pub mod op {
305     bitflags! {
306     /// Holds a set of bit flags representing the actions for the event.
307     ///
308     /// For a list of possible values, have a look at the [notify::op](index.html) documentation.
309     ///
310     /// Multiple actions may be delivered in a single event.
311         pub struct Op: u32 {
312     /// Attributes changed
313             const CHMOD       = 0b000_0001;
314     /// Created
315             const CREATE      = 0b000_0010;
316     /// Removed
317             const REMOVE      = 0b000_0100;
318     /// Renamed
319             const RENAME      = 0b000_1000;
320     /// Written
321             const WRITE       = 0b001_0000;
322     /// File opened for writing was closed
323             const CLOSE_WRITE = 0b010_0000;
324     /// Directories need to be rescanned
325             const RESCAN      = 0b100_0000;
326         }
327     }
328 
329     pub const CHMOD: Op = Op::CHMOD;
330     pub const CREATE: Op = Op::CREATE;
331     pub const REMOVE: Op = Op::REMOVE;
332     pub const RENAME: Op = Op::RENAME;
333     pub const WRITE: Op = Op::WRITE;
334     pub const CLOSE_WRITE: Op = Op::CLOSE_WRITE;
335     pub const RESCAN: Op = Op::RESCAN;
336 }
337 
338 #[cfg(test)]
339 mod op_test {
340     #[test]
mixed_bitflags_form()341     fn mixed_bitflags_form() {
342         let op = super::op::Op::CHMOD | super::op::WRITE;
343         assert!(op.contains(super::op::CHMOD));
344         assert!(op.contains(super::op::Op::WRITE));
345     }
346 
347     #[test]
new_bitflags_form()348     fn new_bitflags_form() {
349         let op = super::op::Op::CHMOD | super::op::Op::WRITE;
350         assert!(op.contains(super::op::Op::WRITE));
351     }
352 
353     #[test]
old_bitflags_form()354     fn old_bitflags_form() {
355         let op = super::op::CHMOD | super::op::WRITE;
356         assert!(op.contains(super::op::WRITE));
357     }
358 }
359 
360 /// Event delivered when action occurs on a watched path in _raw_ mode
361 #[derive(Debug)]
362 pub struct RawEvent {
363     /// Path where the event originated.
364     ///
365     /// `path` is always absolute, even if a relative path is used to watch a file or directory.
366     ///
367     /// On **macOS** the path is always canonicalized.
368     ///
369     /// Keep in mind that the path may be false if the watched file or directory or any parent
370     /// directory is renamed. (See: [notify::op](op/index.html#rename))
371     pub path: Option<PathBuf>,
372 
373     /// Operation detected on that path.
374     ///
375     /// When using the `PollWatcher`, `op` may be `Err` if reading meta data for the path fails.
376     ///
377     /// When using the `INotifyWatcher`, `op` may be `Err` if activity is detected on the file and
378     /// there is an error reading from inotify.
379     pub op: Result<Op>,
380 
381     /// Unique cookie associating related events (for `RENAME` events).
382     ///
383     /// If two consecutive `RENAME` events share the same cookie, it means that the first event
384     /// holds the old path, and the second event holds the new path of the renamed file or
385     /// directory.
386     ///
387     /// For details on handling `RENAME` events with the `FsEventWatcher` have a look at the
388     /// [notify::op](op/index.html) documentation.
389     pub cookie: Option<u32>,
390 }
391 
392 unsafe impl Send for RawEvent {}
393 
394 #[derive(Debug)]
395 /// Event delivered when action occurs on a watched path in debounced mode
396 pub enum DebouncedEvent {
397     /// `NoticeWrite` is emitted immediately after the first write event for the path.
398     ///
399     /// If you are reading from that file, you should probably close it immediately and discard all
400     /// data you read from it.
401     NoticeWrite(PathBuf),
402 
403     /// `NoticeRemove` is emitted immediately after a remove or rename event for the path.
404     ///
405     /// The file will continue to exist until its last file handle is closed.
406     NoticeRemove(PathBuf),
407 
408     /// `Create` is emitted when a file or directory has been created and no events were detected
409     /// for the path within the specified time frame.
410     ///
411     /// `Create` events have a higher priority than `Write` and `Chmod`. These events will not be
412     /// emitted if they are detected before the `Create` event has been emitted.
413     Create(PathBuf),
414 
415     /// `Write` is emitted when a file has been written to and no events were detected for the path
416     /// within the specified time frame.
417     ///
418     /// `Write` events have a higher priority than `Chmod`. `Chmod` will not be emitted if it's
419     /// detected before the `Write` event has been emitted.
420     ///
421     /// Upon receiving a `Create` event for a directory, it is necessary to scan the newly created
422     /// directory for contents. The directory can contain files or directories if those contents
423     /// were created before the directory could be watched, or if the directory was moved into the
424     /// watched directory.
425     Write(PathBuf),
426 
427     /// `Chmod` is emitted when attributes have been changed and no events were detected for the
428     /// path within the specified time frame.
429     Chmod(PathBuf),
430 
431     /// `Remove` is emitted when a file or directory has been removed and no events were detected
432     /// for the path within the specified time frame.
433     Remove(PathBuf),
434 
435     /// `Rename` is emitted when a file or directory has been moved within a watched directory and
436     /// no events were detected for the new path within the specified time frame.
437     ///
438     /// The first path contains the source, the second path the destination.
439     Rename(PathBuf, PathBuf),
440 
441     /// `Rescan` is emitted immediately after a problem has been detected that makes it necessary
442     /// to re-scan the watched directories.
443     Rescan,
444 
445     /// `Error` is emitted immediately after a error has been detected.
446     ///
447     ///  This event may contain a path for which the error was detected.
448     Error(Error, Option<PathBuf>),
449 }
450 
451 impl PartialEq for DebouncedEvent {
eq(&self, other: &DebouncedEvent) -> bool452     fn eq(&self, other: &DebouncedEvent) -> bool {
453         match (self, other) {
454             (&DebouncedEvent::NoticeWrite(ref a), &DebouncedEvent::NoticeWrite(ref b))
455             | (&DebouncedEvent::NoticeRemove(ref a), &DebouncedEvent::NoticeRemove(ref b))
456             | (&DebouncedEvent::Create(ref a), &DebouncedEvent::Create(ref b))
457             | (&DebouncedEvent::Write(ref a), &DebouncedEvent::Write(ref b))
458             | (&DebouncedEvent::Chmod(ref a), &DebouncedEvent::Chmod(ref b))
459             | (&DebouncedEvent::Remove(ref a), &DebouncedEvent::Remove(ref b)) => a == b,
460             (&DebouncedEvent::Rename(ref a1, ref a2), &DebouncedEvent::Rename(ref b1, ref b2)) => {
461                 (a1 == b1 && a2 == b2)
462             }
463             (&DebouncedEvent::Rescan, &DebouncedEvent::Rescan) => true,
464             _ => false,
465         }
466     }
467 }
468 
469 /// Errors generated from the `notify` crate
470 #[derive(Debug)]
471 pub enum Error {
472     /// Generic error
473     ///
474     /// May be used in cases where a platform specific error is mapped to this type
475     Generic(String),
476 
477     /// I/O errors
478     Io(io::Error),
479 
480     /// The provided path does not exist
481     PathNotFound,
482 
483     /// Attempted to remove a watch that does not exist
484     WatchNotFound,
485 }
486 
487 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result488     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
489         let error = String::from(match *self {
490             Error::PathNotFound => "No path was found.",
491             Error::WatchNotFound => "No watch was found.",
492             Error::Generic(ref err) => err.as_ref(),
493             Error::Io(ref err) => err.description(),
494         });
495 
496         write!(f, "{}", error)
497     }
498 }
499 
500 /// Type alias to use this library's `Error` type in a Result
501 pub type Result<T> = StdResult<T, Error>;
502 
503 impl StdError for Error {
description(&self) -> &str504     fn description(&self) -> &str {
505         match *self {
506             Error::PathNotFound => "No path was found",
507             Error::WatchNotFound => "No watch was found",
508             Error::Generic(_) => "Generic error",
509             Error::Io(_) => "I/O Error",
510         }
511     }
512 
cause(&self) -> Option<&StdError>513     fn cause(&self) -> Option<&StdError> {
514         match *self {
515             Error::Io(ref cause) => Some(cause),
516             _ => None,
517         }
518     }
519 }
520 
521 impl From<io::Error> for Error {
from(err: io::Error) -> Error522     fn from(err: io::Error) -> Error {
523         // do not report inotify limits as "no more space" on linux #266
524         #[cfg(target_os = "linux")]
525         {
526             if err.raw_os_error() == Some(28) {
527                 return Error::Generic(String::from("Can't watch (more) files, limit on the total number of inotify watches reached"))
528             }
529         }
530         Error::Io(err)
531     }
532 }
533 
534 /// Indicates whether only the provided directory or its sub-directories as well should be watched
535 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
536 pub enum RecursiveMode {
537     /// Watch all sub-directories as well, including directories created after installing the watch
538     Recursive,
539 
540     /// Watch only the provided directory
541     NonRecursive,
542 }
543 
544 impl RecursiveMode {
is_recursive(&self) -> bool545     fn is_recursive(&self) -> bool {
546         match *self {
547             RecursiveMode::Recursive => true,
548             RecursiveMode::NonRecursive => false,
549         }
550     }
551 }
552 
553 /// Type that can deliver file activity notifications
554 ///
555 /// Watcher is implemented per platform using the best implementation available on that platform.
556 /// In addition to such event driven implementations, a polling implementation is also provided
557 /// that should work on any platform.
558 pub trait Watcher: Sized {
559     /// Create a new watcher in _raw_ mode.
560     ///
561     /// Events will be sent using the provided `tx` immediately after they occurred.
new_raw(tx: Sender<RawEvent>) -> Result<Self>562     fn new_raw(tx: Sender<RawEvent>) -> Result<Self>;
563 
564     /// Create a new _debounced_ watcher with a `delay`.
565     ///
566     /// Events won't be sent immediately but after the specified delay.
567     ///
568     /// # Advantages
569     ///
570     /// This has the advantage that a lot of logic can be offloaded to `notify`.
571     ///
572     /// For example you won't have to handle `RENAME` events yourself by piecing the two parts of
573     /// rename events together. Instead you will just receive a `Rename{from: PathBuf, to:
574     /// PathBuf}` event.
575     ///
576     /// Also `notify` will detect the beginning and the end of write operations. As soon as
577     /// something is written to a file, a `NoticeWrite` event is emitted. If no new event arrived
578     /// until after the specified `delay`, a `Write` event is emitted.
579     ///
580     /// A practical example would be the safe-saving of a file, where a temporary file is created
581     /// and written to, then only when everything has been written to that file is it renamed to
582     /// overwrite the file that was meant to be saved. Instead of receiving a `CREATE` event for
583     /// the temporary file, `WRITE` events to that file and a `RENAME` event from the temporary
584     /// file to the file being saved, you will just receive a single `Write` event.
585     ///
586     /// If you use a delay of more than 30 seconds, you can avoid receiving repetitions of previous
587     /// events on macOS.
588     ///
589     /// # Disadvantages
590     ///
591     /// Your application might not feel as responsive.
592     ///
593     /// If a file is saved very slowly, you might receive a `Write` event even though the file is
594     /// still being written to.
new(tx: Sender<DebouncedEvent>, delay: Duration) -> Result<Self>595     fn new(tx: Sender<DebouncedEvent>, delay: Duration) -> Result<Self>;
596 
597     /// Begin watching a new path.
598     ///
599     /// If the `path` is a directory, `recursive_mode` will be evaluated. If `recursive_mode` is
600     /// `RecursiveMode::Recursive` events will be delivered for all files in that tree. Otherwise
601     /// only the directory and its immediate children will be watched.
602     ///
603     /// If the `path` is a file, `recursive_mode` will be ignored and events will be delivered only
604     /// for the file.
605     ///
606     /// On some platforms, if the `path` is renamed or removed while being watched, behaviour may
607     /// be unexpected. See discussions in [#165] and [#166]. If less surprising behaviour is wanted
608     /// one may non-recursively watch the _parent_ directory as well and manage related events.
609     ///
610     /// [#165]: https://github.com/notify-rs/notify/issues/165
611     /// [#166]: https://github.com/notify-rs/notify/issues/166
watch<P: AsRef<Path>>(&mut self, path: P, recursive_mode: RecursiveMode) -> Result<()>612     fn watch<P: AsRef<Path>>(&mut self, path: P, recursive_mode: RecursiveMode) -> Result<()>;
613 
614     /// Stop watching a path.
615     ///
616     /// # Errors
617     ///
618     /// Returns an error in the case that `path` has not been watched or if removing the watch
619     /// fails.
unwatch<P: AsRef<Path>>(&mut self, path: P) -> Result<()>620     fn unwatch<P: AsRef<Path>>(&mut self, path: P) -> Result<()>;
621 }
622 
623 /// The recommended `Watcher` implementation for the current platform
624 #[cfg(target_os = "linux")]
625 pub type RecommendedWatcher = INotifyWatcher;
626 /// The recommended `Watcher` implementation for the current platform
627 #[cfg(target_os = "macos")]
628 pub type RecommendedWatcher = FsEventWatcher;
629 /// The recommended `Watcher` implementation for the current platform
630 #[cfg(target_os = "windows")]
631 pub type RecommendedWatcher = ReadDirectoryChangesWatcher;
632 /// The recommended `Watcher` implementation for the current platform
633 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
634 pub type RecommendedWatcher = PollWatcher;
635 
636 /// Convenience method for creating the `RecommendedWatcher` for the current platform in _raw_ mode.
637 ///
638 /// See [`Watcher::new_raw`](trait.Watcher.html#tymethod.new_raw).
raw_watcher(tx: Sender<RawEvent>) -> Result<RecommendedWatcher>639 pub fn raw_watcher(tx: Sender<RawEvent>) -> Result<RecommendedWatcher> {
640     Watcher::new_raw(tx)
641 }
642 
643 /// Convenience method for creating the `RecommendedWatcher` for the current
644 /// platform in default (debounced) mode.
645 ///
646 /// See [`Watcher::new`](trait.Watcher.html#tymethod.new).
watcher(tx: Sender<DebouncedEvent>, delay: Duration) -> Result<RecommendedWatcher>647 pub fn watcher(tx: Sender<DebouncedEvent>, delay: Duration) -> Result<RecommendedWatcher> {
648     Watcher::new(tx, delay)
649 }
650 
651 #[test]
display_formatted_errors()652 fn display_formatted_errors() {
653     let expected = "Some error";
654 
655     assert_eq!(
656         expected,
657         format!("{}", Error::Generic(String::from(expected)))
658     );
659 
660     assert_eq!(
661         expected,
662         format!(
663             "{}",
664             Error::Io(io::Error::new(io::ErrorKind::Other, expected))
665         )
666     );
667 }
668