1 //! Error types definitions.
3 //! All usage errors will pass throught the Error enum, a lot of them in the Error::Custom.
5 use std::fmt::{self, Display};
7 use crate::utils::colors::*;
10 /// All errors that can be generated by `ouch`
11 #[derive(Debug, PartialEq)]
13 /// Not every IoError, some of them get filtered by `From<io::Error>` into other variants
14 IoError { reason: String },
15 /// From lzzzz::lz4f::Error
16 Lz4Error { reason: String },
17 /// Detected from io::Error if .kind() is io::ErrorKind::NotFound
18 NotFound { error_title: String },
19 /// NEEDS MORE CONTEXT
20 AlreadyExists { error_title: String },
21 /// From zip::result::ZipError::InvalidArchive
22 InvalidZipArchive(&'static str),
23 /// Detected from io::Error if .kind() is io::ErrorKind::PermissionDenied
24 PermissionDenied { error_title: String },
25 /// From zip::result::ZipError::UnsupportedArchive
26 UnsupportedZipArchive(&'static str),
28 CompressingRootFolder,
29 /// Specialized walkdir's io::Error wrapper with additional information on the error
30 WalkdirError { reason: String },
31 /// Custom and unique errors are reported in this variant
32 Custom { reason: FinalError },
35 /// Alias to std's Result with ouch's Error
36 pub type Result<T> = std::result::Result<T, Error>;
38 /// Pretty final error message for end users, crashing the program after display.
39 #[derive(Clone, Debug, Default, PartialEq)]
40 pub struct FinalError {
41 /// Should be made of just one line, appears after the "\[ERROR\]" part
43 /// Shown as a unnumbered list in yellow
45 /// Shown as green at the end to give hints on how to work around this error, if it's fixable
49 impl Display for FinalError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 // When in ACCESSIBLE mode, the square brackets are suppressed
54 if *crate::cli::ACCESSIBLE.get().unwrap_or(&false) {
55 write!(f, "{}ERROR{}: {}", *RED, *RESET, self.title)?;
57 write!(f, "{}[ERROR]{} {}", *RED, *RESET, self.title)?;
61 for detail in &self.details {
62 write!(f, "\n - {}{}{}", *YELLOW, detail, *RESET)?;
66 if !self.hints.is_empty() {
67 // Separate by one blank line.
69 // to reduce redundant output for text-to-speach systems, braille
70 // displays and so on, only print "hints" once in ACCESSIBLE mode
71 if *crate::cli::ACCESSIBLE.get().unwrap_or(&false) {
72 write!(f, "\n{}hints:{}", *GREEN, *RESET)?;
73 for hint in &self.hints {
74 write!(f, "\n{}", hint)?;
77 for hint in &self.hints {
78 write!(f, "\n{}hint:{} {}", *GREEN, *RESET, hint)?;
89 pub fn with_title(title: impl ToString) -> Self {
90 Self { title: title.to_string(), details: vec![], hints: vec![] }
93 /// Add one detail line, can have multiple
94 pub fn detail(mut self, detail: impl ToString) -> Self {
95 self.details.push(detail.to_string());
99 /// Add one hint line, can have multiple
100 pub fn hint(mut self, hint: impl ToString) -> Self {
101 self.hints.push(hint.to_string());
106 impl fmt::Display for Error {
107 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108 let err = match self {
109 Error::WalkdirError { reason } => FinalError::with_title(reason),
110 Error::NotFound { error_title } => FinalError::with_title(error_title).detail("File not found"),
111 Error::CompressingRootFolder => {
112 FinalError::with_title("It seems you're trying to compress the root folder.")
113 .detail("This is unadvisable since ouch does compressions in-memory.")
114 .hint("Use a more appropriate tool for this, such as rsync.")
116 Error::IoError { reason } => FinalError::with_title(reason),
117 Error::Lz4Error { reason } => FinalError::with_title(reason),
118 Error::AlreadyExists { error_title } => FinalError::with_title(error_title).detail("File already exists"),
119 Error::InvalidZipArchive(reason) => FinalError::with_title("Invalid zip archive").detail(reason),
120 Error::PermissionDenied { error_title } => FinalError::with_title(error_title).detail("Permission denied"),
121 Error::UnsupportedZipArchive(reason) => FinalError::with_title("Unsupported zip archive").detail(reason),
122 Error::Custom { reason } => reason.clone(),
129 impl From<std::io::Error> for Error {
130 fn from(err: std::io::Error) -> Self {
132 std::io::ErrorKind::NotFound => Self::NotFound { error_title: err.to_string() },
133 std::io::ErrorKind::PermissionDenied => Self::PermissionDenied { error_title: err.to_string() },
134 std::io::ErrorKind::AlreadyExists => Self::AlreadyExists { error_title: err.to_string() },
135 _other => Self::IoError { reason: err.to_string() },
140 impl From<lzzzz::lz4f::Error> for Error {
141 fn from(err: lzzzz::lz4f::Error) -> Self {
142 Self::Lz4Error { reason: err.to_string() }
146 impl From<zip::result::ZipError> for Error {
147 fn from(err: zip::result::ZipError) -> Self {
148 use zip::result::ZipError;
150 ZipError::Io(io_err) => Self::from(io_err),
151 ZipError::InvalidArchive(filename) => Self::InvalidZipArchive(filename),
152 ZipError::FileNotFound => {
154 reason: FinalError::with_title("Unexpected error in zip archive").detail("File not found"),
157 ZipError::UnsupportedArchive(filename) => Self::UnsupportedZipArchive(filename),
162 impl From<walkdir::Error> for Error {
163 fn from(err: walkdir::Error) -> Self {
164 Self::WalkdirError { reason: err.to_string() }
168 impl From<FinalError> for Error {
169 fn from(err: FinalError) -> Self {
170 Self::Custom { reason: err }