1use crate::{Colors, Dimension, Orientation};
2use std::{
3 io,
4 num::{ParseIntError, TryFromIntError},
5 path::PathBuf,
6 string::FromUtf8Error,
7};
8use thiserror::Error;
9
10pub type WallSwitchResult<T> = Result<T, WallSwitchError>;
18
19#[derive(Error, Debug)]
25pub enum WallSwitchError {
26 #[error(
28 "{e}: '{a}' value '{v}' must be at least '{n}'. \
29 The condition ({v} >= {n}) is false.\n\n\
30 For more information, try '{h}'.",
31 e = "Error".red().bold(),
32 v = value.yellow(),
33 a = arg.yellow(),
34 n = num.green(),
35 h = "--help".green(),
36 )]
37 AtLeastValue {
38 arg: String,
39 value: String,
40 num: u64,
41 },
42
43 #[error(
45 "{e}: '{a}' value '{v}' must be at most '{n}'. \
46 The condition ({v} <= {n}) is false.\n\n\
47 For more information, try '{h}'.",
48 e = "Error".red().bold(),
49 v = value.yellow(),
50 a = arg.yellow(),
51 n = num.green(),
52 h = "--help".green(),
53 )]
54 AtMostValue {
55 arg: String,
56 value: String,
57 num: u64,
58 },
59
60 #[error("Disregard the path: '{p}'.", p = .0.display().yellow(),)]
62 DisregardPath(PathBuf),
63
64 #[error("Failed to convert command output to UTF-8: {0}")]
66 FromUtf8(#[from] FromUtf8Error),
67
68 #[error("IO error: {0}")]
70 Io(#[from] io::Error),
71
72 #[error("{0}")]
74 InvalidDimension(#[from] DimensionError),
75
76 #[error(
78 "{e}: Invalid file name --> Disregard the path: '{p}'.",
79 e = "Error".red().bold(),
80 p = .0.display().yellow(),
81 )]
82 InvalidFilename(PathBuf),
83
84 #[error(
86 "{e}: invalid file size '{s}' bytes. \
87 The condition ({min} <= {s} <= {max}) is false.",
88 e = "Error".red().bold(),
89 min = min_size.green(),
90 max = max_size.green(),
91 s = size.yellow(),
92 )]
93 InvalidSize {
94 min_size: u64,
95 size: u64,
96 max_size: u64,
97 },
98
99 #[error(
101 "{e}: invalid value '{v}' for '{a}'.\n\n\
102 For more information, try '{h}'.",
103 e = "Error".red().bold(),
104 v = value.yellow(),
105 a = arg.green(),
106 h = "--help".green(),
107 )]
108 InvalidValue { arg: String, value: String },
109
110 #[error("{e}: Failed to create file {path:?}\n{io_error}", e = "Error".red().bold())]
112 IOError {
113 path: PathBuf,
114 #[source]
115 io_error: io::Error,
116 },
117
118 #[error("JSON serialization/deserialization error: {0}")]
120 Json(#[from] serde_json::Error),
121
122 #[error("Unable to obtain maximum value!")]
124 MaxValue,
125
126 #[error("Unable to obtain minimum value!")]
128 MinValue,
129
130 #[error(
132 "{e}: missing value for '{a}'.\n\n\
133 For more information, try '{h}'.",
134 e = "Error".red().bold(),
135 a = arg.yellow(),
136 h = "--help".green(),
137 )]
138 MissingValue { arg: String },
139
140 #[error(
142 "{e}: min ({min}) must be less than or equal to max ({max})\n\
143 The condition ({min} <= {max}) is false.",
144 e = "Error".red().bold()
145 )]
146 MinMax { min: u64, max: u64 },
147
148 #[error(
150 "{e}: no images found in image directories!\n\
151 directories: {paths:#?}",
152 e = "Error".red().bold(),
153 )]
154 NoImages { paths: Vec<PathBuf> },
155
156 #[error(
158 "invalid orientation.\n\n\
159 Valid options: [{h}, {v}]",
160 h = Orientation::Horizontal.green(),
161 v = Orientation::Vertical.green(),
162 )]
163 InvalidOrientation,
164
165 #[error(
167 "{e}: insufficient number of image files!\n\
168 Found only {n} image file(s):\n\
169 {paths:#?}",
170 n = nfiles.yellow(),
171 e = "Error".red().bold(),
172 )]
173 InsufficientImages { paths: Vec<PathBuf>, nfiles: usize },
174
175 #[error("Insufficient number of valid images!")]
177 InsufficientNumber,
178
179 #[error("Wallpaper dir {0:?} does not exist.")]
181 Parent(PathBuf),
182
183 #[error("{0}")]
185 TryInto(String),
186
187 #[error("Unable to find '{0}'!")]
189 UnableToFind(String),
190
191 #[error(
193 "{e}: unexpected argument '{a}' found.\n\n\
194 For more information, try '{h}'.",
195 e = "Error".red().bold(),
196 a = arg.yellow(),
197 h = "--help".green(),
198 )]
199 UnexpectedArg { arg: String },
200}
201
202impl From<TryFromIntError> for WallSwitchError {
207 fn from(err: TryFromIntError) -> Self {
208 Self::TryInto(err.to_string())
209 }
210}
211
212#[derive(Error, Debug)]
213pub enum DimensionError {
214 #[error(
215 "{error}: invalid dimension '{dimension}'.\n\
216 {log_min}{log_max}\
217 Disregard the path: '{path}'\n",
218 error = "Error".red().bold(),
219 dimension = .dimension.yellow(),
220 log_min = .log_min,
221 log_max = .log_max,
222 path = .path.display().yellow(),
223 )]
224 DimensionFormatError {
225 dimension: Dimension,
226 log_min: String,
227 log_max: String,
228 path: PathBuf,
229 },
230
231 #[error("Invalid dimension format '{0}': failed to parse integer - {1}")]
232 InvalidParse(String, #[source] ParseIntError),
233
234 #[error("Invalid dimension format: expected two numbers (width x height)")]
235 InvalidFormat,
236
237 #[error("Zero is not a valid dimension component")]
238 ZeroDimension,
239}
240
241#[cfg(test)]
242mod error_tests {
243 use crate::{Colors, WallSwitchError};
244 use std::path::PathBuf;
245
246 #[test]
247 fn test_error_display() {
249 let path = PathBuf::from("/tmp");
250 let text = format!("Disregard the path: '{}'.", path.display().yellow());
251 println!("text: {text}");
252
253 assert_eq!(
254 WallSwitchError::InsufficientNumber.to_string(),
255 "Insufficient number of valid images!"
256 );
257
258 assert_eq!(WallSwitchError::DisregardPath(path).to_string(), text);
259 }
260}