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