1use crate::Config;
4use std::error::Error as StdError;
5use std::path::PathBuf;
6use std::result::Result as StdResult;
7use std::{self, fmt, io};
8
9pub type Result<T> = StdResult<T, Error>;
11
12#[derive(Debug)]
14pub enum ErrorKind {
15 Generic(String),
20
21 Io(io::Error),
23
24 PathNotFound,
26
27 WatchNotFound,
29
30 InvalidConfig(Config),
32
33 MaxFilesWatch,
35}
36
37#[derive(Debug)]
45pub struct Error {
46 pub kind: ErrorKind,
48
49 pub paths: Vec<PathBuf>,
51}
52
53impl Error {
54 #[must_use]
56 pub fn add_path(mut self, path: PathBuf) -> Self {
57 self.paths.push(path);
58 self
59 }
60
61 #[must_use]
63 pub fn set_paths(mut self, paths: Vec<PathBuf>) -> Self {
64 self.paths = paths;
65 self
66 }
67
68 #[must_use]
70 pub fn new(kind: ErrorKind) -> Self {
71 Self {
72 kind,
73 paths: Vec::new(),
74 }
75 }
76
77 #[must_use]
79 pub fn generic(msg: &str) -> Self {
80 Self::new(ErrorKind::Generic(msg.into()))
81 }
82
83 #[must_use]
85 pub fn io(err: io::Error) -> Self {
86 Self::new(ErrorKind::Io(err))
87 }
88
89 #[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 #[must_use]
101 pub fn path_not_found() -> Self {
102 Self::new(ErrorKind::PathNotFound)
103 }
104
105 #[must_use]
107 pub fn watch_not_found() -> Self {
108 Self::new(ErrorKind::WatchNotFound)
109 }
110
111 #[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}