1 //! Error type definitions.
3 //! All the unexpected user-side errors should be treated in this file, that does not include
4 //! errors made by devs in our implementation.
6 //! TODO: wrap `FinalError` in a variant to keep all `FinalError::display_and_crash()` function
7 //! calls inside of this module.
11 path::{Path, PathBuf},
14 use crate::{oof, utils::colors::*};
16 #[derive(Debug, PartialEq)]
18 UnknownExtensionError(String),
19 MissingExtensionError(PathBuf),
20 IoError { reason: String },
21 FileNotFound(PathBuf),
23 InvalidZipArchive(&'static str),
25 UnsupportedZipArchive(&'static str),
27 OofError(oof::OofError),
28 CompressingRootFolder,
29 MissingArgumentsForCompression,
30 MissingArgumentsForDecompression,
32 WalkdirError { reason: String },
33 Custom { reason: FinalError },
36 pub type Result<T> = std::result::Result<T, Error>;
38 #[derive(Clone, Debug, Default, PartialEq)]
39 pub struct FinalError {
45 impl Display for FinalError {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 writeln!(f, "{}[ERROR]{} {}", red(), reset(), self.title)?;
51 for detail in &self.details {
52 writeln!(f, " {}-{} {}", white(), yellow(), detail)?;
56 if !self.hints.is_empty() {
57 // Separate by one blank line.
59 for hint in &self.hints {
60 writeln!(f, "{}hint:{} {}", green(), reset(), hint)?;
64 write!(f, "{}", reset())
69 pub fn with_title(title: impl ToString) -> Self {
70 Self { title: title.to_string(), details: vec![], hints: vec![] }
73 pub fn detail(&mut self, detail: impl ToString) -> &mut Self {
74 self.details.push(detail.to_string());
78 pub fn hint(&mut self, hint: impl ToString) -> &mut Self {
79 self.hints.push(hint.to_string());
84 impl fmt::Display for Error {
85 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86 let err = match self {
87 Error::MissingExtensionError(filename) => {
88 let error = FinalError::with_title(format!("Cannot compress to {:?}", filename))
89 .detail("Ouch could not detect the compression format")
90 .hint("Use a supported format extension, like '.zip' or '.tar.gz'")
91 .hint("Check https://github.com/vrmiguel/ouch for a full list of supported formats")
96 Error::WalkdirError { reason } => FinalError::with_title(reason),
97 Error::FileNotFound(file) => {
98 let error = if file == Path::new("") {
99 FinalError::with_title("file not found!")
101 FinalError::with_title(format!("file {:?} not found!", file))
106 Error::CompressingRootFolder => {
107 let error = FinalError::with_title("It seems you're trying to compress the root folder.")
108 .detail("This is unadvisable since ouch does compressions in-memory.")
109 .hint("Use a more appropriate tool for this, such as rsync.")
114 Error::MissingArgumentsForCompression => {
115 let error = FinalError::with_title("Could not compress")
116 .detail("The compress command requires at least 2 arguments")
117 .hint("You must provide:")
118 .hint(" - At least one input argument.")
119 .hint(" - The output argument.")
121 .hint("Example: `ouch compress image.png img.zip`")
126 Error::MissingArgumentsForDecompression => {
127 let error = FinalError::with_title("Could not decompress")
128 .detail("The compress command requires at least one argument")
129 .hint("You must provide:")
130 .hint(" - At least one input argument.")
132 .hint("Example: `ouch decompress imgs.tar.gz`")
137 Error::InternalError => {
138 let error = FinalError::with_title("InternalError :(")
139 .detail("This should not have happened")
140 .detail("It's probably our fault")
141 .detail("Please help us improve by reporting the issue at:")
142 .detail(format!(" {}https://github.com/vrmiguel/ouch/issues ", cyan()))
147 Error::OofError(err) => FinalError::with_title(err),
148 Error::IoError { reason } => FinalError::with_title(reason),
149 Error::CompressionTypo => FinalError::with_title("Possible typo detected")
150 .hint(format!("Did you mean '{}ouch compress{}'?", magenta(), reset()))
152 Error::UnknownExtensionError(_) => todo!(),
153 Error::AlreadyExists => todo!(),
154 Error::InvalidZipArchive(_) => todo!(),
155 Error::PermissionDenied => todo!(),
156 Error::UnsupportedZipArchive(_) => todo!(),
157 Error::Custom { reason } => reason.clone(),
165 pub fn with_reason(reason: FinalError) -> Self {
166 Self::Custom { reason }
170 impl From<std::io::Error> for Error {
171 fn from(err: std::io::Error) -> Self {
173 std::io::ErrorKind::NotFound => panic!("{}", err),
174 std::io::ErrorKind::PermissionDenied => Self::PermissionDenied,
175 std::io::ErrorKind::AlreadyExists => Self::AlreadyExists,
176 _other => Self::IoError { reason: err.to_string() },
181 impl From<zip::result::ZipError> for Error {
182 fn from(err: zip::result::ZipError) -> Self {
183 use zip::result::ZipError::*;
185 Io(io_err) => Self::from(io_err),
186 InvalidArchive(filename) => Self::InvalidZipArchive(filename),
187 FileNotFound => Self::FileNotFound("".into()),
188 UnsupportedArchive(filename) => Self::UnsupportedZipArchive(filename),
193 impl From<walkdir::Error> for Error {
194 fn from(err: walkdir::Error) -> Self {
195 Self::WalkdirError { reason: err.to_string() }
199 impl From<oof::OofError> for Error {
200 fn from(err: oof::OofError) -> Self {