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 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 /// Creates a new "path not found" error.
85 pub fn path_not_found() -> Self {
86 Self::new(ErrorKind::PathNotFound)
87 }
88
89 /// Creates a new "watch not found" error.
90 pub fn watch_not_found() -> Self {
91 Self::new(ErrorKind::WatchNotFound)
92 }
93
94 /// Creates a new "invalid config" error from the given `Config`.
95 pub fn invalid_config(config: &Config) -> Self {
96 Self::new(ErrorKind::InvalidConfig(config.clone()))
97 }
98}
99
100impl fmt::Display for Error {
101 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102 let error: String = match self.kind {
103 ErrorKind::PathNotFound => "No path was found.".into(),
104 ErrorKind::WatchNotFound => "No watch was found.".into(),
105 ErrorKind::InvalidConfig(ref config: &Config) => format!("Invalid configuration: {:?}", config),
106 ErrorKind::Generic(ref err: &String) => err.clone(),
107 ErrorKind::Io(ref err: &Error) => err.to_string(),
108 ErrorKind::MaxFilesWatch => "OS file watch limit reached.".into(),
109 };
110
111 if self.paths.is_empty() {
112 write!(f, "{}", error)
113 } else {
114 write!(f, "{} about {:?}", error, self.paths)
115 }
116 }
117}
118
119impl StdError for Error {
120 fn cause(&self) -> Option<&dyn StdError> {
121 match self.kind {
122 ErrorKind::Io(ref cause: &Error) => Some(cause),
123 _ => None,
124 }
125 }
126}
127
128impl From<io::Error> for Error {
129 fn from(err: io::Error) -> Self {
130 Error::io(err)
131 }
132}
133
134#[cfg(feature = "crossbeam-channel")]
135impl<T> From<crossbeam_channel::SendError<T>> for Error {
136 fn from(err: crossbeam_channel::SendError<T>) -> Self {
137 Error::generic(&format!("internal channel disconnect: {:?}", err))
138 }
139}
140#[cfg(not(feature = "crossbeam-channel"))]
141impl<T> From<std::sync::mpsc::SendError<T>> for Error {
142 fn from(err: std::sync::mpsc::SendError<T>) -> Self {
143 Error::generic(&format!("internal channel disconnect: {:?}", err))
144 }
145}
146#[cfg(feature = "crossbeam-channel")]
147impl From<crossbeam_channel::RecvError> for Error {
148 fn from(err: crossbeam_channel::RecvError) -> Self {
149 Error::generic(&format!("internal channel disconnect: {:?}", err))
150 }
151}
152#[cfg(not(feature = "crossbeam-channel"))]
153impl From<std::sync::mpsc::RecvError> for Error {
154 fn from(err: std::sync::mpsc::RecvError) -> Self {
155 Error::generic(&format!("internal channel disconnect: {:?}", err))
156 }
157}
158
159impl<T> From<std::sync::PoisonError<T>> for Error {
160 fn from(err: std::sync::PoisonError<T>) -> Self {
161 Error::generic(&format!("internal mutex poisoned: {:?}", err))
162 }
163}
164
165#[test]
166fn display_formatted_errors() {
167 let expected: &str = "Some error";
168
169 assert_eq!(expected, format!("{}", Error::generic(expected)));
170
171 assert_eq!(
172 expected,
173 format!(
174 "{}",
175 Error::io(io::Error::new(io::ErrorKind::Other, expected))
176 )
177 );
178}
179