1use crate::{Config, PathOp};
4use std::error::Error as StdError;
5use std::fmt::Debug;
6use std::path::PathBuf;
7use std::result::Result as StdResult;
8use std::{self, fmt, io};
9
10pub type Result<T> = StdResult<T, Error>;
12
13#[derive(Debug)]
15pub enum ErrorKind {
16 Generic(String),
21
22 Io(io::Error),
24
25 PathNotFound,
27
28 WatchNotFound,
30
31 InvalidConfig(Config),
33
34 MaxFilesWatch,
36}
37
38#[derive(Debug)]
46pub struct Error {
47 pub kind: ErrorKind,
49
50 pub paths: Vec<PathBuf>,
52}
53
54impl Error {
55 #[must_use]
57 pub fn add_path(mut self, path: PathBuf) -> Self {
58 self.paths.push(path);
59 self
60 }
61
62 #[must_use]
64 pub fn set_paths(mut self, paths: Vec<PathBuf>) -> Self {
65 self.paths = paths;
66 self
67 }
68
69 #[must_use]
71 pub fn new(kind: ErrorKind) -> Self {
72 Self {
73 kind,
74 paths: Vec::new(),
75 }
76 }
77
78 #[must_use]
80 pub fn generic(msg: &str) -> Self {
81 Self::new(ErrorKind::Generic(msg.into()))
82 }
83
84 #[must_use]
86 pub fn io(err: io::Error) -> Self {
87 Self::new(ErrorKind::Io(err))
88 }
89
90 #[must_use]
92 pub fn io_watch(err: io::Error) -> Self {
93 if err.kind() == io::ErrorKind::NotFound {
94 Self::path_not_found()
95 } else {
96 Self::io(err)
97 }
98 }
99
100 #[must_use]
102 pub fn path_not_found() -> Self {
103 Self::new(ErrorKind::PathNotFound)
104 }
105
106 #[must_use]
108 pub fn watch_not_found() -> Self {
109 Self::new(ErrorKind::WatchNotFound)
110 }
111
112 #[must_use]
114 pub fn invalid_config(config: &Config) -> Self {
115 Self::new(ErrorKind::InvalidConfig(*config))
116 }
117}
118
119impl fmt::Display for Error {
120 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
121 let error = match self.kind {
122 ErrorKind::PathNotFound => "No path was found.".into(),
123 ErrorKind::WatchNotFound => "No watch was found.".into(),
124 ErrorKind::InvalidConfig(ref config) => format!("Invalid configuration: {config:?}"),
125 ErrorKind::Generic(ref err) => err.clone(),
126 ErrorKind::Io(ref err) => err.to_string(),
127 ErrorKind::MaxFilesWatch => "OS file watch limit reached.".into(),
128 };
129
130 if self.paths.is_empty() {
131 write!(f, "{error}")
132 } else {
133 write!(f, "{} about {:?}", error, self.paths)
134 }
135 }
136}
137
138impl StdError for Error {
139 fn cause(&self) -> Option<&dyn StdError> {
140 match self.kind {
141 ErrorKind::Io(ref cause) => Some(cause),
142 _ => None,
143 }
144 }
145}
146
147impl From<io::Error> for Error {
148 fn from(err: io::Error) -> Self {
149 Error::io(err)
150 }
151}
152
153impl<T> From<std::sync::mpsc::SendError<T>> for Error {
154 fn from(err: std::sync::mpsc::SendError<T>) -> Self {
155 Error::generic(&format!("internal channel disconnect: {err:?}"))
156 }
157}
158
159impl From<std::sync::mpsc::RecvError> for Error {
160 fn from(err: std::sync::mpsc::RecvError) -> Self {
161 Error::generic(&format!("internal channel disconnect: {err:?}"))
162 }
163}
164
165impl<T> From<std::sync::PoisonError<T>> for Error {
166 fn from(err: std::sync::PoisonError<T>) -> Self {
167 Error::generic(&format!("internal mutex poisoned: {err:?}"))
168 }
169}
170
171#[derive(Debug)]
177pub struct UpdatePathsError {
178 pub source: Error,
180
181 pub origin: Option<PathOp>,
187
188 pub remaining: Vec<PathOp>,
193}
194
195impl fmt::Display for UpdatePathsError {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 write!(f, "unable to apply the batch operation: {}", self.source)
198 }
199}
200
201impl StdError for UpdatePathsError {
202 fn source(&self) -> Option<&(dyn StdError + 'static)> {
203 Some(&self.source)
204 }
205}
206
207impl From<UpdatePathsError> for Error {
208 fn from(value: UpdatePathsError) -> Self {
209 value.source
210 }
211}
212
213impl IntoIterator for UpdatePathsError {
214 type Item = PathOp;
215
216 type IntoIter = std::iter::Chain<std::option::IntoIter<PathOp>, std::vec::IntoIter<PathOp>>;
217
218 fn into_iter(self) -> Self::IntoIter {
219 self.origin.into_iter().chain(self.remaining)
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn display_formatted_errors() {
229 let expected = "Some error";
230
231 assert_eq!(expected, format!("{}", Error::generic(expected)));
232
233 assert_eq!(
234 expected,
235 format!("{}", Error::io(io::Error::other(expected)))
236 );
237 }
238
239 #[test]
240 fn display_update_paths() {
241 let actual = UpdatePathsError {
242 source: Error::generic("Some error"),
243 origin: None,
244 remaining: Default::default(),
245 }
246 .to_string();
247
248 assert_eq!(
249 format!("unable to apply the batch operation: Some error"),
250 actual
251 );
252 }
253}