1 | //! Error types |
2 | |
3 | use crate::Config; |
4 | use std::error::Error as StdError; |
5 | use std::path::PathBuf; |
6 | use std::result::Result as StdResult; |
7 | use std::{self, fmt, io}; |
8 | |
9 | /// Type alias to use this library's `Error` type in a Result |
10 | pub type Result<T> = StdResult<T, Error>; |
11 | |
12 | /// Error kinds |
13 | #[derive (Debug)] |
14 | pub 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)] |
45 | pub 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 | |
53 | impl Error { |
54 | /// Adds a path to the error. |
55 | pub fn add_path(mut self, path: PathBuf) -> Self { |
56 | self.paths.push(path); |
57 | self |
58 | } |
59 | |
60 | /// Replaces the paths for the error. |
61 | pub fn set_paths(mut self, paths: Vec<PathBuf>) -> Self { |
62 | self.paths = paths; |
63 | self |
64 | } |
65 | |
66 | /// Creates a new Error with empty paths given its kind. |
67 | pub fn new(kind: ErrorKind) -> Self { |
68 | Self { |
69 | kind, |
70 | paths: Vec::new(), |
71 | } |
72 | } |
73 | |
74 | /// Creates a new generic Error from a message. |
75 | pub fn generic(msg: &str) -> Self { |
76 | Self::new(ErrorKind::Generic(msg.into())) |
77 | } |
78 | |
79 | /// Creates a new i/o Error from a stdlib `io::Error`. |
80 | pub fn io(err: io::Error) -> Self { |
81 | Self::new(ErrorKind::Io(err)) |
82 | } |
83 | |
84 | /// Similar to [`Error::io`], but specifically handles [`io::ErrorKind::NotFound`]. |
85 | pub fn io_watch(err: io::Error) -> Self { |
86 | if err.kind() == io::ErrorKind::NotFound { |
87 | Self::path_not_found() |
88 | } else { |
89 | Self::io(err) |
90 | } |
91 | } |
92 | |
93 | /// Creates a new "path not found" error. |
94 | pub fn path_not_found() -> Self { |
95 | Self::new(ErrorKind::PathNotFound) |
96 | } |
97 | |
98 | /// Creates a new "watch not found" error. |
99 | pub fn watch_not_found() -> Self { |
100 | Self::new(ErrorKind::WatchNotFound) |
101 | } |
102 | |
103 | /// Creates a new "invalid config" error from the given `Config`. |
104 | pub fn invalid_config(config: &Config) -> Self { |
105 | Self::new(ErrorKind::InvalidConfig(*config)) |
106 | } |
107 | } |
108 | |
109 | impl fmt::Display for Error { |
110 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
111 | let error: String = match self.kind { |
112 | ErrorKind::PathNotFound => "No path was found." .into(), |
113 | ErrorKind::WatchNotFound => "No watch was found." .into(), |
114 | ErrorKind::InvalidConfig(ref config: &Config) => format!("Invalid configuration: {:?}" , config), |
115 | ErrorKind::Generic(ref err: &String) => err.clone(), |
116 | ErrorKind::Io(ref err: &Error) => err.to_string(), |
117 | ErrorKind::MaxFilesWatch => "OS file watch limit reached." .into(), |
118 | }; |
119 | |
120 | if self.paths.is_empty() { |
121 | write!(f, " {}" , error) |
122 | } else { |
123 | write!(f, " {} about {:?}" , error, self.paths) |
124 | } |
125 | } |
126 | } |
127 | |
128 | impl StdError for Error { |
129 | fn cause(&self) -> Option<&dyn StdError> { |
130 | match self.kind { |
131 | ErrorKind::Io(ref cause: &Error) => Some(cause), |
132 | _ => None, |
133 | } |
134 | } |
135 | } |
136 | |
137 | impl From<io::Error> for Error { |
138 | fn from(err: io::Error) -> Self { |
139 | Error::io(err) |
140 | } |
141 | } |
142 | |
143 | impl<T> From<std::sync::mpsc::SendError<T>> for Error { |
144 | fn from(err: std::sync::mpsc::SendError<T>) -> Self { |
145 | Error::generic(&format!("internal channel disconnect: {:?}" , err)) |
146 | } |
147 | } |
148 | |
149 | impl From<std::sync::mpsc::RecvError> for Error { |
150 | fn from(err: std::sync::mpsc::RecvError) -> Self { |
151 | Error::generic(&format!("internal channel disconnect: {:?}" , err)) |
152 | } |
153 | } |
154 | |
155 | impl<T> From<std::sync::PoisonError<T>> for Error { |
156 | fn from(err: std::sync::PoisonError<T>) -> Self { |
157 | Error::generic(&format!("internal mutex poisoned: {:?}" , err)) |
158 | } |
159 | } |
160 | |
161 | #[test ] |
162 | fn display_formatted_errors() { |
163 | let expected = "Some error" ; |
164 | |
165 | assert_eq!(expected, format!("{}" , Error::generic(expected))); |
166 | |
167 | assert_eq!( |
168 | expected, |
169 | format!( |
170 | "{}" , |
171 | Error::io(io::Error::new(io::ErrorKind::Other, expected)) |
172 | ) |
173 | ); |
174 | } |
175 | |