Skip to main content

notify/
error.rs

1//! Error types
2
3use crate::Config;
4use std::error::Error as StdError;
5use std::path::PathBuf;
6use std::result::Result as StdResult;
7use std::{self, fmt, io};
8
9/// Type alias to use this library's `Error` type in a Result
10pub type Result<T> = StdResult<T, Error>;
11
12/// Error kinds
13#[derive(Debug)]
14pub enum ErrorKind {
15    /// Generic error
16    ///
17    /// May be used in cases where a platform specific error is mapped to this type, or for opaque
18    /// internal errors.
19    Generic(String),
20
21    /// I/O errors.
22    Io(io::Error),
23
24    /// A path does not exist.
25    PathNotFound,
26
27    /// Attempted to remove a watch that does not exist.
28    WatchNotFound,
29
30    /// An invalid value was passed as runtime configuration.
31    InvalidConfig(Config),
32
33    /// Can't watch (more) files, limit on the total number of inotify watches reached
34    MaxFilesWatch,
35}
36
37/// Notify error type.
38///
39/// Errors are emitted either at creation time of a `Watcher`, or during the event stream. They
40/// range from kernel errors to filesystem errors to argument errors.
41///
42/// Errors can be general, or they can be about specific paths or subtrees. In that later case, the
43/// error's `paths` field will be populated.
44#[derive(Debug)]
45pub struct Error {
46    /// Kind of the error.
47    pub kind: ErrorKind,
48
49    /// Relevant paths to the error, if any.
50    pub paths: Vec<PathBuf>,
51}
52
53impl Error {
54    /// Adds a path to the error.
55    #[must_use]
56    pub fn add_path(mut self, path: PathBuf) -> Self {
57        self.paths.push(path);
58        self
59    }
60
61    /// Replaces the paths for the error.
62    #[must_use]
63    pub fn set_paths(mut self, paths: Vec<PathBuf>) -> Self {
64        self.paths = paths;
65        self
66    }
67
68    /// Creates a new Error with empty paths given its kind.
69    #[must_use]
70    pub fn new(kind: ErrorKind) -> Self {
71        Self {
72            kind,
73            paths: Vec::new(),
74        }
75    }
76
77    /// Creates a new generic Error from a message.
78    #[must_use]
79    pub fn generic(msg: &str) -> Self {
80        Self::new(ErrorKind::Generic(msg.into()))
81    }
82
83    /// Creates a new i/o Error from a stdlib `io::Error`.
84    #[must_use]
85    pub fn io(err: io::Error) -> Self {
86        Self::new(ErrorKind::Io(err))
87    }
88
89    /// Similar to [`Error::io`], but specifically handles [`io::ErrorKind::NotFound`].
90    #[must_use]
91    pub fn io_watch(err: io::Error) -> Self {
92        if err.kind() == io::ErrorKind::NotFound {
93            Self::path_not_found()
94        } else {
95            Self::io(err)
96        }
97    }
98
99    /// Creates a new "path not found" error.
100    #[must_use]
101    pub fn path_not_found() -> Self {
102        Self::new(ErrorKind::PathNotFound)
103    }
104
105    /// Creates a new "watch not found" error.
106    #[must_use]
107    pub fn watch_not_found() -> Self {
108        Self::new(ErrorKind::WatchNotFound)
109    }
110
111    /// Creates a new "invalid config" error from the given `Config`.
112    #[must_use]
113    pub fn invalid_config(config: &Config) -> Self {
114        Self::new(ErrorKind::InvalidConfig(*config))
115    }
116}
117
118impl fmt::Display for Error {
119    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120        let error = match self.kind {
121            ErrorKind::PathNotFound => "No path was found.".into(),
122            ErrorKind::WatchNotFound => "No watch was found.".into(),
123            ErrorKind::InvalidConfig(ref config) => format!("Invalid configuration: {config:?}"),
124            ErrorKind::Generic(ref err) => err.clone(),
125            ErrorKind::Io(ref err) => err.to_string(),
126            ErrorKind::MaxFilesWatch => "OS file watch limit reached.".into(),
127        };
128
129        if self.paths.is_empty() {
130            write!(f, "{error}")
131        } else {
132            write!(f, "{} about {:?}", error, self.paths)
133        }
134    }
135}
136
137impl StdError for Error {
138    fn cause(&self) -> Option<&dyn StdError> {
139        match self.kind {
140            ErrorKind::Io(ref cause) => Some(cause),
141            _ => None,
142        }
143    }
144}
145
146impl From<io::Error> for Error {
147    fn from(err: io::Error) -> Self {
148        Error::io(err)
149    }
150}
151
152impl<T> From<std::sync::mpsc::SendError<T>> for Error {
153    fn from(err: std::sync::mpsc::SendError<T>) -> Self {
154        Error::generic(&format!("internal channel disconnect: {err:?}"))
155    }
156}
157
158impl From<std::sync::mpsc::RecvError> for Error {
159    fn from(err: std::sync::mpsc::RecvError) -> Self {
160        Error::generic(&format!("internal channel disconnect: {err:?}"))
161    }
162}
163
164impl<T> From<std::sync::PoisonError<T>> for Error {
165    fn from(err: std::sync::PoisonError<T>) -> Self {
166        Error::generic(&format!("internal mutex poisoned: {err:?}"))
167    }
168}
169
170#[test]
171fn display_formatted_errors() {
172    let expected = "Some error";
173
174    assert_eq!(expected, format!("{}", Error::generic(expected)));
175
176    assert_eq!(
177        expected,
178        format!("{}", Error::io(io::Error::other(expected)))
179    );
180}