1 //! Error types definitions.
3 //! All usage errors will pass throught the Error enum, a lot of them in the Error::Custom.
10 use crate::utils::colors::*;
12 #[allow(missing_docs)]
13 /// All errors that can be generated by `ouch`
14 #[derive(Debug, PartialEq)]
16 /// Not every IoError, some of them get filtered by `From<io::Error>` into other variants
17 IoError { reason: String },
18 /// From lzzzz::lz4f::Error
19 Lz4Error { reason: String },
20 /// Detected from io::Error if .kind() is io::ErrorKind::NotFound
21 NotFound { error_title: String },
22 /// NEEDS MORE CONTEXT
23 AlreadyExists { error_title: String },
24 /// From zip::result::ZipError::InvalidArchive
25 InvalidZipArchive(&'static str),
26 /// Detected from io::Error if .kind() is io::ErrorKind::PermissionDenied
27 PermissionDenied { error_title: String },
28 /// From zip::result::ZipError::UnsupportedArchive
29 UnsupportedZipArchive(&'static str),
31 CompressingRootFolder,
32 /// Specialized walkdir's io::Error wrapper with additional information on the error
33 WalkdirError { reason: String },
34 /// Custom and unique errors are reported in this variant
35 Custom { reason: FinalError },
38 /// Alias to std's Result with ouch's Error
39 pub type Result<T> = std::result::Result<T, Error>;
41 /// A string either heap-allocated or located in static storage
42 pub type CowStr = Cow<'static, str>;
44 /// Pretty final error message for end users, crashing the program after display.
45 #[derive(Clone, Debug, Default, PartialEq)]
46 pub struct FinalError {
47 /// Should be made of just one line, appears after the "\[ERROR\]" part
49 /// Shown as a unnumbered list in yellow
51 /// Shown as green at the end to give hints on how to work around this error, if it's fixable
55 impl Display for FinalError {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 // When in ACCESSIBLE mode, the square brackets are suppressed
60 if *crate::cli::ACCESSIBLE.get().unwrap_or(&false) {
61 write!(f, "{}ERROR{}: {}", *RED, *RESET, self.title)?;
63 write!(f, "{}[ERROR]{} {}", *RED, *RESET, self.title)?;
67 for detail in &self.details {
68 write!(f, "\n - {}{}{}", *YELLOW, detail, *RESET)?;
72 if !self.hints.is_empty() {
73 // Separate by one blank line.
75 // to reduce redundant output for text-to-speach systems, braille
76 // displays and so on, only print "hints" once in ACCESSIBLE mode
77 if *crate::cli::ACCESSIBLE.get().unwrap_or(&false) {
78 write!(f, "\n{}hints:{}", *GREEN, *RESET)?;
79 for hint in &self.hints {
80 write!(f, "\n{}", hint)?;
83 for hint in &self.hints {
84 write!(f, "\n{}hint:{} {}", *GREEN, *RESET, hint)?;
96 pub fn with_title(title: impl Into<CowStr>) -> Self {
97 Self { title: title.into(), details: vec![], hints: vec![] }
100 /// Add one detail line, can have multiple
102 pub fn detail(mut self, detail: impl Into<CowStr>) -> Self {
103 self.details.push(detail.into());
107 /// Add one hint line, can have multiple
109 pub fn hint(mut self, hint: impl Into<CowStr>) -> Self {
110 self.hints.push(hint.into());
115 impl fmt::Display for Error {
116 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117 let err = match self {
118 Error::WalkdirError { reason } => FinalError::with_title(reason.to_string()),
119 Error::NotFound { error_title } => FinalError::with_title(error_title.to_string()).detail("File not found"),
120 Error::CompressingRootFolder => {
121 FinalError::with_title("It seems you're trying to compress the root folder.")
122 .detail("This is unadvisable since ouch does compressions in-memory.")
123 .hint("Use a more appropriate tool for this, such as rsync.")
125 Error::IoError { reason } => FinalError::with_title(reason.to_string()),
126 Error::Lz4Error { reason } => FinalError::with_title(reason.to_string()),
127 Error::AlreadyExists { error_title } => {
128 FinalError::with_title(error_title.to_string()).detail("File already exists")
130 Error::InvalidZipArchive(reason) => FinalError::with_title("Invalid zip archive").detail(*reason),
131 Error::PermissionDenied { error_title } => {
132 FinalError::with_title(error_title.to_string()).detail("Permission denied")
134 Error::UnsupportedZipArchive(reason) => FinalError::with_title("Unsupported zip archive").detail(*reason),
135 Error::Custom { reason } => reason.clone(),
142 impl From<std::io::Error> for Error {
143 fn from(err: std::io::Error) -> Self {
145 std::io::ErrorKind::NotFound => Self::NotFound { error_title: err.to_string() },
146 std::io::ErrorKind::PermissionDenied => Self::PermissionDenied { error_title: err.to_string() },
147 std::io::ErrorKind::AlreadyExists => Self::AlreadyExists { error_title: err.to_string() },
148 _other => Self::IoError { reason: err.to_string() },
153 impl From<lzzzz::lz4f::Error> for Error {
154 fn from(err: lzzzz::lz4f::Error) -> Self {
155 Self::Lz4Error { reason: err.to_string() }
159 impl From<zip::result::ZipError> for Error {
160 fn from(err: zip::result::ZipError) -> Self {
161 use zip::result::ZipError;
163 ZipError::Io(io_err) => Self::from(io_err),
164 ZipError::InvalidArchive(filename) => Self::InvalidZipArchive(filename),
165 ZipError::FileNotFound => {
167 reason: FinalError::with_title("Unexpected error in zip archive").detail("File not found"),
170 ZipError::UnsupportedArchive(filename) => Self::UnsupportedZipArchive(filename),
175 impl From<ignore::Error> for Error {
176 fn from(err: ignore::Error) -> Self {
177 Self::WalkdirError { reason: err.to_string() }
181 impl From<FinalError> for Error {
182 fn from(err: FinalError) -> Self {
183 Self::Custom { reason: err }