| 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 | |