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 {
52 write!(f, "{}[ERROR]{} {}", *RED, *RESET, self.title)?;
55 for detail in &self.details {
56 write!(f, "\n - {}{}{}", *YELLOW, detail, *RESET)?;
60 if !self.hints.is_empty() {
61 // Separate by one blank line.
63 for hint in &self.hints {
64 write!(f, "\n{}hint:{} {}", *GREEN, *RESET, hint)?;
74 pub fn with_title(title: impl ToString) -> Self {
75 Self { title: title.to_string(), details: vec![], hints: vec![] }
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());
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());
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.")
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(),
114 impl From<std::io::Error> for Error {
115 fn from(err: std::io::Error) -> Self {
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() },
125 impl From<lzzzz::lz4f::Error> for Error {
126 fn from(err: lzzzz::lz4f::Error) -> Self {
127 Self::Lz4Error { reason: err.to_string() }
131 impl From<zip::result::ZipError> for Error {
132 fn from(err: zip::result::ZipError) -> Self {
133 use zip::result::ZipError;
135 ZipError::Io(io_err) => Self::from(io_err),
136 ZipError::InvalidArchive(filename) => Self::InvalidZipArchive(filename),
137 ZipError::FileNotFound => {
139 reason: FinalError::with_title("Unexpected error in zip archive").detail("File not found"),
142 ZipError::UnsupportedArchive(filename) => Self::UnsupportedZipArchive(filename),
147 impl From<walkdir::Error> for Error {
148 fn from(err: walkdir::Error) -> Self {
149 Self::WalkdirError { reason: err.to_string() }
153 impl From<FinalError> for Error {
154 fn from(err: FinalError) -> Self {
155 Self::Custom { reason: err }