Merge pull request #206 from sigmaSd/refactor
[ouch.git] / src / error.rs
blob289efbc26a6da3f827a6e313b74589d141bfc498
1 //! Error types definitions.
2 //!
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::*;
9 #[allow(missing_docs)]
10 /// All errors that can be generated by `ouch`
11 #[derive(Debug, PartialEq)]
12 pub enum Error {
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),
27     /// TO BE REMOVED
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
42     title: String,
43     /// Shown as a unnumbered list in yellow
44     details: Vec<String>,
45     /// Shown as green at the end to give hints on how to work around this error, if it's fixable
46     hints: Vec<String>,
49 impl Display for FinalError {
50     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51         // Title
52         write!(f, "{}[ERROR]{} {}", *RED, *RESET, self.title)?;
54         // Details
55         for detail in &self.details {
56             write!(f, "\n - {}{}{}", *YELLOW, detail, *RESET)?;
57         }
59         // Hints
60         if !self.hints.is_empty() {
61             // Separate by one blank line.
62             writeln!(f)?;
63             for hint in &self.hints {
64                 write!(f, "\n{}hint:{} {}", *GREEN, *RESET, hint)?;
65             }
66         }
68         Ok(())
69     }
72 impl FinalError {
73     /// Only constructor
74     pub fn with_title(title: impl ToString) -> Self {
75         Self { title: title.to_string(), details: vec![], hints: vec![] }
76     }
78     /// Add one detail line, can have multiple
79     pub fn detail(mut self, detail: impl ToString) -> Self {
80         self.details.push(detail.to_string());
81         self
82     }
84     /// Add one hint line, can have multiple
85     pub fn hint(mut self, hint: impl ToString) -> Self {
86         self.hints.push(hint.to_string());
87         self
88     }
91 impl fmt::Display for Error {
92     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93         let err = match self {
94             Error::WalkdirError { reason } => FinalError::with_title(reason),
95             Error::NotFound { error_title } => FinalError::with_title(error_title).detail("File not found"),
96             Error::CompressingRootFolder => {
97                 FinalError::with_title("It seems you're trying to compress the root folder.")
98                     .detail("This is unadvisable since ouch does compressions in-memory.")
99                     .hint("Use a more appropriate tool for this, such as rsync.")
100             }
101             Error::IoError { reason } => FinalError::with_title(reason),
102             Error::Lz4Error { reason } => FinalError::with_title(reason),
103             Error::AlreadyExists { error_title } => FinalError::with_title(error_title).detail("File already exists"),
104             Error::InvalidZipArchive(reason) => FinalError::with_title("Invalid zip archive").detail(reason),
105             Error::PermissionDenied { error_title } => FinalError::with_title(error_title).detail("Permission denied"),
106             Error::UnsupportedZipArchive(reason) => FinalError::with_title("Unsupported zip archive").detail(reason),
107             Error::Custom { reason } => reason.clone(),
108         };
110         write!(f, "{}", err)
111     }
114 impl From<std::io::Error> for Error {
115     fn from(err: std::io::Error) -> Self {
116         match err.kind() {
117             std::io::ErrorKind::NotFound => Self::NotFound { error_title: err.to_string() },
118             std::io::ErrorKind::PermissionDenied => Self::PermissionDenied { error_title: err.to_string() },
119             std::io::ErrorKind::AlreadyExists => Self::AlreadyExists { error_title: err.to_string() },
120             _other => Self::IoError { reason: err.to_string() },
121         }
122     }
125 impl From<lzzzz::lz4f::Error> for Error {
126     fn from(err: lzzzz::lz4f::Error) -> Self {
127         Self::Lz4Error { reason: err.to_string() }
128     }
131 impl From<zip::result::ZipError> for Error {
132     fn from(err: zip::result::ZipError) -> Self {
133         use zip::result::ZipError;
134         match err {
135             ZipError::Io(io_err) => Self::from(io_err),
136             ZipError::InvalidArchive(filename) => Self::InvalidZipArchive(filename),
137             ZipError::FileNotFound => {
138                 Self::Custom {
139                     reason: FinalError::with_title("Unexpected error in zip archive").detail("File not found"),
140                 }
141             }
142             ZipError::UnsupportedArchive(filename) => Self::UnsupportedZipArchive(filename),
143         }
144     }
147 impl From<walkdir::Error> for Error {
148     fn from(err: walkdir::Error) -> Self {
149         Self::WalkdirError { reason: err.to_string() }
150     }
153 impl From<FinalError> for Error {
154     fn from(err: FinalError) -> Self {
155         Self::Custom { reason: err }
156     }