+202 -133 +/-5 browse
1 | diff --git a/meli/src/mail/compose/hooks.rs b/meli/src/mail/compose/hooks.rs |
2 | index 013b791..0f86890 100644 |
3 | --- a/meli/src/mail/compose/hooks.rs |
4 | +++ b/meli/src/mail/compose/hooks.rs |
5 | @@ -22,7 +22,7 @@ |
6 | //! Pre-submission hooks for draft validation and/or transformations. |
7 | pub use std::borrow::Cow; |
8 | |
9 | - use melib::email::headers::HeaderName; |
10 | + use melib::{email::headers::HeaderName, src_err_arc_wrap}; |
11 | |
12 | use super::*; |
13 | |
14 | @@ -102,11 +102,11 @@ impl Hook { |
15 | .stderr(Stdio::piped()) |
16 | .spawn() |
17 | .map_err(|err| -> Error { |
18 | - format!( |
19 | + Error::new(format!( |
20 | "could not execute `{command}`. Check if its binary is in PATH or if \ |
21 | - the command is valid. Original error: {err}" |
22 | - ) |
23 | - .into() |
24 | + the command is valid." |
25 | + )) |
26 | + .set_source(Some(src_err_arc_wrap! {err})) |
27 | })?; |
28 | let mut stdin = child |
29 | .stdin |
30 | @@ -121,7 +121,8 @@ impl Hook { |
31 | }); |
32 | }); |
33 | let output = child.wait_with_output().map_err(|err| -> Error { |
34 | - format!("failed to wait on hook child {name_}: {err}").into() |
35 | + Error::new(format!("failed to wait on hook child {name_}")) |
36 | + .set_source(Some(src_err_arc_wrap! {err})) |
37 | })?; |
38 | let stdout = String::from_utf8_lossy(&output.stdout); |
39 | let stderr = String::from_utf8_lossy(&output.stderr); |
40 | @@ -187,7 +188,10 @@ fn important_header_warn(_ctx: &mut Context, draft: &mut Draft) -> Result<()> { |
41 | for hdr in [HeaderName::FROM, HeaderName::TO] { |
42 | match draft.headers.get(&hdr).map(melib::Address::list_try_from) { |
43 | Some(Ok(_)) => {} |
44 | - Some(Err(err)) => return Err(format!("{hdr} header value is invalid ({err}).").into()), |
45 | + Some(Err(err)) => { |
46 | + return Err(Error::new(format!("{hdr} header value is invalid")) |
47 | + .set_source(Some(src_err_arc_wrap! {err}))) |
48 | + } |
49 | None => return Err(format!("{hdr} header is missing and should be present.").into()), |
50 | } |
51 | } |
52 | @@ -198,8 +202,11 @@ fn important_header_warn(_ctx: &mut Context, draft: &mut Draft) -> Result<()> { |
53 | .get(HeaderName::DATE) |
54 | .map(melib::utils::datetime::rfc822_to_timestamp) |
55 | { |
56 | - Some(Err(err)) => return Err(format!("Date header value is invalid ({err}).").into()), |
57 | - Some(Ok(0)) => return Err("Date header value is invalid.".into()), |
58 | + Some(Err(err)) => { |
59 | + return Err(Error::new("Date header value is invalid.") |
60 | + .set_source(Some(src_err_arc_wrap! {err}))) |
61 | + } |
62 | + Some(Ok(0)) => return Err(Error::new("Date header value is invalid.")), |
63 | _ => {} |
64 | } |
65 | } |
66 | @@ -211,7 +218,8 @@ fn important_header_warn(_ctx: &mut Context, draft: &mut Draft) -> Result<()> { |
67 | .filter(|v| !v.trim().is_empty()) |
68 | .map(melib::Address::list_try_from) |
69 | { |
70 | - return Err(format!("{hdr} header value is invalid ({err}).").into()); |
71 | + return Err(Error::new(format!("{hdr} header value is invalid")) |
72 | + .set_source(Some(src_err_arc_wrap! {err}))); |
73 | } |
74 | } |
75 | Ok(()) |
76 | @@ -310,8 +318,9 @@ mod tests { |
77 | let err_msg = hook(&mut ctx, &mut draft).unwrap_err().to_string(); |
78 | assert_eq!( |
79 | err_msg, |
80 | - "From header value is invalid (Parsing error. In input: \"...\",\nError: Alternative, \ |
81 | - Many1, Alternative, atom(): starts with whitespace or empty).", |
82 | + "From header value is invalid\nCaused by:\n[2] Parsing error. In input: \ |
83 | + \"...\",\nError: Alternative, Many1, Alternative, atom(): starts with whitespace or \ |
84 | + empty", |
85 | "HEADERWARN should complain about From value being empty: {}", |
86 | err_msg |
87 | ); |
88 | @@ -321,8 +330,9 @@ mod tests { |
89 | let err_msg = hook(&mut ctx, &mut draft).unwrap_err().to_string(); |
90 | assert_eq!( |
91 | err_msg, |
92 | - "To header value is invalid (Parsing error. In input: \"...\",\nError: Alternative, \ |
93 | - Many1, Alternative, atom(): starts with whitespace or empty).", |
94 | + "To header value is invalid\nCaused by:\n[2] Parsing error. In input: \ |
95 | + \"...\",\nError: Alternative, Many1, Alternative, atom(): starts with whitespace or \ |
96 | + empty", |
97 | "HEADERWARN should complain about To value being empty: {}", |
98 | err_msg |
99 | ); |
100 | @@ -350,8 +360,8 @@ mod tests { |
101 | let err_msg = hook(&mut ctx, &mut draft).unwrap_err().to_string(); |
102 | assert_eq!( |
103 | err_msg, |
104 | - "From header value is invalid (Parsing error. In input: \"user \ |
105 | - user@example.com>...\",\nError: Alternative, Tag).", |
106 | + "From header value is invalid\nCaused by:\n[2] Parsing error. In input: \"user \ |
107 | + user@example.com>...\",\nError: Alternative, Tag", |
108 | "HEADERWARN should complain about From value being invalid: {}", |
109 | err_msg |
110 | ); |
111 | diff --git a/meli/tests/test_cli_subcommands.rs b/meli/tests/test_cli_subcommands.rs |
112 | index 4e6d3d0..e159fe1 100644 |
113 | --- a/meli/tests/test_cli_subcommands.rs |
114 | +++ b/meli/tests/test_cli_subcommands.rs |
115 | @@ -223,7 +223,7 @@ server_password_command = "false" |
116 | output |
117 | .code(1) |
118 | .stderr(predicate::eq( |
119 | - "Edit the sample configuration and relaunch meli.\nKind: Configuration\n", |
120 | + "Configuration error: Edit the sample configuration and relaunch meli.\n", |
121 | )) |
122 | .stdout( |
123 | predicate::eq( |
124 | diff --git a/melib/src/error.rs b/melib/src/error.rs |
125 | index 5e9082b..328e199 100644 |
126 | --- a/melib/src/error.rs |
127 | +++ b/melib/src/error.rs |
128 | @@ -21,7 +21,13 @@ |
129 | |
130 | //! Library error type. |
131 | |
132 | - use std::{borrow::Cow, io, result, str, string, sync::Arc}; |
133 | + use std::{ |
134 | + borrow::Cow, |
135 | + error, io, |
136 | + path::{Path, PathBuf}, |
137 | + result, str, string, |
138 | + sync::Arc, |
139 | + }; |
140 | |
141 | pub type Result<T> = result::Result<T, Error>; |
142 | |
143 | @@ -57,10 +63,10 @@ pub enum ErrorKind { |
144 | impl std::fmt::Display for ErrorKind { |
145 | fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
146 | match self { |
147 | - Self::None => write!(fmt, "None"), |
148 | - Self::External => write!(fmt, "External"), |
149 | - Self::LinkedLibrary(ref name) => write!(fmt, "Linked library error: {name}"), |
150 | - Self::Authentication => write!(fmt, "Authentication"), |
151 | + Self::None => write!(fmt, ""), |
152 | + Self::External => write!(fmt, "External error"), |
153 | + Self::LinkedLibrary(ref name) => write!(fmt, "Linked library `{name}` error"), |
154 | + Self::Authentication => write!(fmt, "Authentication error"), |
155 | Self::Bug => write!(fmt, "Bug, please report this!"), |
156 | Self::Network(ref inner) => write!(fmt, "{}", inner.as_str()), |
157 | Self::ProtocolError => write!(fmt, "Protocol error"), |
158 | @@ -68,14 +74,14 @@ impl std::fmt::Display for ErrorKind { |
159 | fmt, |
160 | "Protocol is not supported. It could be the wrong type or version." |
161 | ), |
162 | - Self::Platform => write!(fmt, "Platform/Runtime environment; OS or hardware"), |
163 | - Self::TimedOut => write!(fmt, "Timed Out"), |
164 | - Self::OSError => write!(fmt, "OS Error"), |
165 | - Self::Configuration => write!(fmt, "Configuration"), |
166 | - Self::NotImplemented => write!(fmt, "Not implemented"), |
167 | - Self::NotSupported => write!(fmt, "Not supported"), |
168 | - Self::NotFound => write!(fmt, "Not found"), |
169 | - Self::ValueError => write!(fmt, "Invalid value"), |
170 | + Self::Platform => write!(fmt, "Platform/Runtime environment error (OS or hardware)"), |
171 | + Self::TimedOut => write!(fmt, "Timed out error"), |
172 | + Self::OSError => write!(fmt, "OS error"), |
173 | + Self::Configuration => write!(fmt, "Configuration error"), |
174 | + Self::NotImplemented => write!(fmt, "Not implemented error"), |
175 | + Self::NotSupported => write!(fmt, "Not supported error"), |
176 | + Self::NotFound => write!(fmt, "Not found error"), |
177 | + Self::ValueError => write!(fmt, "Invalid value error"), |
178 | } |
179 | } |
180 | } |
181 | @@ -133,8 +139,9 @@ macro_rules! src_err_arc_wrap { |
182 | pub struct Error { |
183 | pub summary: Cow<'static, str>, |
184 | pub details: Option<Cow<'static, str>>, |
185 | - pub source: Option<std::sync::Arc<dyn std::error::Error + Send + Sync + 'static>>, |
186 | - pub related_path: Option<std::path::PathBuf>, |
187 | + pub source: Option<Box<Self>>, |
188 | + pub inner: Option<Arc<dyn error::Error + Send + Sync + 'static>>, |
189 | + pub related_path: Option<PathBuf>, |
190 | pub kind: ErrorKind, |
191 | } |
192 | |
193 | @@ -165,7 +172,7 @@ pub trait ResultIntoError<T> { |
194 | |
195 | pub trait WrapResultIntoError<T, I> |
196 | where |
197 | - I: Send + Sync + std::error::Error + 'static, |
198 | + I: Send + Sync + error::Error + 'static, |
199 | { |
200 | /// Wrap a result into a new [`Error`] that sets its source to the original |
201 | /// value. |
202 | @@ -227,7 +234,7 @@ impl<T, I: Into<Error>> ResultIntoError<T> for std::result::Result<T, I> { |
203 | } |
204 | |
205 | #[inline] |
206 | - fn chain_err_related_path(self, p: &std::path::Path) -> Result<T> { |
207 | + fn chain_err_related_path(self, p: &Path) -> Result<T> { |
208 | self.map_err(|err| err.set_err_related_path(p)) |
209 | } |
210 | |
211 | @@ -239,7 +246,7 @@ impl<T, I: Into<Error>> ResultIntoError<T> for std::result::Result<T, I> { |
212 | |
213 | impl<T, I> WrapResultIntoError<T, I> for std::result::Result<T, I> |
214 | where |
215 | - I: Send + Sync + std::error::Error + 'static, |
216 | + I: Send + Sync + error::Error + 'static, |
217 | { |
218 | #[inline] |
219 | /// Wrap a result into a new [`Error`] that sets its source to the original |
220 | @@ -262,6 +269,7 @@ impl Error { |
221 | summary: msg.into(), |
222 | details: None, |
223 | source: None, |
224 | + inner: None, |
225 | related_path: None, |
226 | kind: ErrorKind::None, |
227 | } |
228 | @@ -293,18 +301,39 @@ impl Error { |
229 | |
230 | pub fn set_source( |
231 | mut self, |
232 | - new_val: Option<std::sync::Arc<dyn std::error::Error + Send + Sync + 'static>>, |
233 | + new_val: Option<std::sync::Arc<dyn error::Error + Send + Sync + 'static>>, |
234 | ) -> Self { |
235 | - self.source = new_val; |
236 | + self.source = new_val.map(|inner| { |
237 | + Box::new(Self { |
238 | + summary: "".into(), |
239 | + details: None, |
240 | + inner: Some(inner), |
241 | + source: None, |
242 | + related_path: None, |
243 | + kind: ErrorKind::External, |
244 | + }) |
245 | + }); |
246 | self |
247 | } |
248 | |
249 | + pub fn from_inner(inner: std::sync::Arc<dyn error::Error + Send + Sync + 'static>) -> Self { |
250 | + Self { |
251 | + summary: "".into(), |
252 | + details: None, |
253 | + inner: Some(inner), |
254 | + source: None, |
255 | + related_path: None, |
256 | + kind: ErrorKind::External, |
257 | + } |
258 | + } |
259 | + |
260 | pub fn set_kind(mut self, new_val: ErrorKind) -> Self { |
261 | self.kind = new_val; |
262 | self |
263 | } |
264 | |
265 | - pub fn set_related_path(mut self, new_val: Option<std::path::PathBuf>) -> Self { |
266 | + pub fn set_related_path<P: Into<PathBuf>>(mut self, new_val: Option<P>) -> Self { |
267 | + let new_val = new_val.map(Into::into); |
268 | self.related_path = new_val; |
269 | self |
270 | } |
271 | @@ -323,34 +352,70 @@ impl Error { |
272 | || self.kind.is_protocol_not_supported() |
273 | || self.kind.is_value_error()) |
274 | } |
275 | + |
276 | + /// Display error chain to user. |
277 | + fn display_chain(&'_ self) -> impl std::fmt::Display + '_ { |
278 | + ErrorChainDisplay { |
279 | + current: self, |
280 | + counter: 1, |
281 | + } |
282 | + } |
283 | } |
284 | |
285 | impl std::fmt::Display for Error { |
286 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
287 | - write!(f, "Error: {}", self.summary)?; |
288 | - if let Some(details) = self.details.as_ref() { |
289 | - if !details.trim().is_empty() { |
290 | - write!(f, "\n{}", details)?; |
291 | + self.display_chain().fmt(f) |
292 | + } |
293 | + } |
294 | + |
295 | + #[derive(Copy, Clone)] |
296 | + struct ErrorChainDisplay<'e> { |
297 | + current: &'e Error, |
298 | + counter: usize, |
299 | + } |
300 | + |
301 | + impl std::fmt::Display for ErrorChainDisplay<'_> { |
302 | + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
303 | + let mut cur = *self; |
304 | + loop { |
305 | + if cur.counter > 1 { |
306 | + write!(fmt, "[{}] ", cur.counter)?; |
307 | + } |
308 | + match cur.current.kind { |
309 | + ErrorKind::External | ErrorKind::None => {} |
310 | + other => write!(fmt, "{other}: ")?, |
311 | + } |
312 | + if let Some(ref inner) = cur.current.inner { |
313 | + write!(fmt, "{}", inner)?; |
314 | + } else { |
315 | + write!(fmt, "{}", cur.current.summary)?; |
316 | + if let Some(details) = cur.current.details.as_ref() { |
317 | + if !details.trim().is_empty() { |
318 | + write!(fmt, "\n{}", details)?; |
319 | + } |
320 | + } |
321 | + if let Some(ref path) = cur.current.related_path { |
322 | + write!(fmt, "\nRelated path: {}", path.display())?; |
323 | + } |
324 | + } |
325 | + if let Some(ref source) = cur.current.source { |
326 | + writeln!(fmt, "\nCaused by:")?; |
327 | + cur = Self { |
328 | + current: source, |
329 | + counter: cur.counter + 1, |
330 | + }; |
331 | + } else { |
332 | + return Ok(()); |
333 | } |
334 | } |
335 | - if let Some(ref path) = self.related_path { |
336 | - write!(f, "\nPath: {}", path.display())?; |
337 | - } |
338 | - if let Some(ref source) = self.source { |
339 | - write!(f, "\nCaused by: {}", source)?; |
340 | - } |
341 | - if self.kind != ErrorKind::None { |
342 | - write!(f, "\nError kind: {}", self.kind)?; |
343 | - } |
344 | - Ok(()) |
345 | } |
346 | } |
347 | |
348 | - impl std::error::Error for Error { |
349 | - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
350 | + impl error::Error for Error { |
351 | + fn source(&self) -> Option<&(dyn error::Error + 'static)> { |
352 | self.source |
353 | .as_ref() |
354 | - .map(|s| &(*(*s)) as &(dyn std::error::Error + 'static)) |
355 | + .map(|s| &(*(*s)) as &(dyn error::Error + 'static)) |
356 | } |
357 | } |
358 | |
359 | @@ -381,54 +446,43 @@ impl From<io::Error> for Error { |
360 | } else { |
361 | err.kind().into() |
362 | }; |
363 | - Self::new(s) |
364 | - .set_details(err.kind().to_string()) |
365 | - .set_source(Some(Arc::new(err))) |
366 | - .set_kind(kind) |
367 | + Self::from_inner(Arc::new(err)).set_kind(kind) |
368 | } |
369 | } |
370 | |
371 | impl<'a> From<Cow<'a, str>> for Error { |
372 | #[inline] |
373 | - fn from(kind: Cow<'_, str>) -> Self { |
374 | - Self::new(kind.to_string()) |
375 | + fn from(err: Cow<'_, str>) -> Self { |
376 | + Self::new(err.to_string()) |
377 | } |
378 | } |
379 | |
380 | impl From<string::FromUtf8Error> for Error { |
381 | #[inline] |
382 | - fn from(kind: string::FromUtf8Error) -> Self { |
383 | - Self::new(kind.to_string()).set_source(Some(Arc::new(kind))) |
384 | + fn from(err: string::FromUtf8Error) -> Self { |
385 | + Self::from_inner(Arc::new(err)).set_kind(ErrorKind::ValueError) |
386 | } |
387 | } |
388 | |
389 | impl From<str::Utf8Error> for Error { |
390 | #[inline] |
391 | - fn from(kind: str::Utf8Error) -> Self { |
392 | - Self::new(kind.to_string()).set_source(Some(Arc::new(kind))) |
393 | + fn from(err: str::Utf8Error) -> Self { |
394 | + Self::from_inner(Arc::new(err)).set_kind(ErrorKind::ValueError) |
395 | } |
396 | } |
397 | - //use std::option; |
398 | - //impl From<option::NoneError> for Error { |
399 | - // #[inline] |
400 | - // fn from(kind: option::NoneError) -> Error { |
401 | - // Error::new(format!("{:?}", kind)) |
402 | - // } |
403 | - //} |
404 | |
405 | impl<T> From<std::sync::PoisonError<T>> for Error { |
406 | #[inline] |
407 | - fn from(kind: std::sync::PoisonError<T>) -> Self { |
408 | - Self::new(kind.to_string()).set_kind(ErrorKind::Bug) |
409 | + fn from(err: std::sync::PoisonError<T>) -> Self { |
410 | + Self::new(err.to_string()).set_kind(ErrorKind::Bug) |
411 | } |
412 | } |
413 | |
414 | #[cfg(feature = "tls")] |
415 | impl<T: Sync + Send + 'static + std::fmt::Debug> From<native_tls::HandshakeError<T>> for Error { |
416 | #[inline] |
417 | - fn from(kind: native_tls::HandshakeError<T>) -> Self { |
418 | - Self::new(kind.to_string()) |
419 | - .set_source(Some(Arc::new(kind))) |
420 | + fn from(err: native_tls::HandshakeError<T>) -> Self { |
421 | + Self::from_inner(Arc::new(err)) |
422 | .set_kind(ErrorKind::Network(NetworkErrorKind::InvalidTLSConnection)) |
423 | } |
424 | } |
425 | @@ -436,24 +490,23 @@ impl<T: Sync + Send + 'static + std::fmt::Debug> From<native_tls::HandshakeError |
426 | #[cfg(feature = "tls")] |
427 | impl From<native_tls::Error> for Error { |
428 | #[inline] |
429 | - fn from(kind: native_tls::Error) -> Self { |
430 | - Self::new(kind.to_string()) |
431 | - .set_source(Some(Arc::new(kind))) |
432 | + fn from(err: native_tls::Error) -> Self { |
433 | + Self::from_inner(Arc::new(err)) |
434 | .set_kind(ErrorKind::Network(NetworkErrorKind::InvalidTLSConnection)) |
435 | } |
436 | } |
437 | |
438 | impl From<std::num::ParseIntError> for Error { |
439 | #[inline] |
440 | - fn from(kind: std::num::ParseIntError) -> Self { |
441 | - Self::new(kind.to_string()).set_source(Some(Arc::new(kind))) |
442 | + fn from(err: std::num::ParseIntError) -> Self { |
443 | + Self::new(err.to_string()).set_kind(ErrorKind::ValueError) |
444 | } |
445 | } |
446 | |
447 | impl From<std::fmt::Error> for Error { |
448 | #[inline] |
449 | - fn from(kind: std::fmt::Error) -> Self { |
450 | - Self::new(kind.to_string()).set_source(Some(Arc::new(kind))) |
451 | + fn from(err: std::fmt::Error) -> Self { |
452 | + Self::new(err.to_string()).set_kind(ErrorKind::Bug) |
453 | } |
454 | } |
455 | |
456 | @@ -462,46 +515,44 @@ impl From<isahc::Error> for Error { |
457 | #[inline] |
458 | fn from(val: isahc::Error) -> Self { |
459 | let kind: NetworkErrorKind = val.kind().into(); |
460 | - Self::new(val.to_string()) |
461 | - .set_source(Some(Arc::new(val))) |
462 | - .set_kind(ErrorKind::Network(kind)) |
463 | + Self::from_inner(Arc::new(val)).set_kind(ErrorKind::Network(kind)) |
464 | } |
465 | } |
466 | |
467 | #[cfg(feature = "jmap")] |
468 | impl From<serde_json::error::Error> for Error { |
469 | #[inline] |
470 | - fn from(kind: serde_json::error::Error) -> Self { |
471 | - Self::new(kind.to_string()).set_source(Some(Arc::new(kind))) |
472 | + fn from(err: serde_json::error::Error) -> Self { |
473 | + Self::from_inner(Arc::new(err)) |
474 | } |
475 | } |
476 | |
477 | - impl From<Box<dyn std::error::Error + Sync + Send + 'static>> for Error { |
478 | + impl From<Box<dyn error::Error + Sync + Send + 'static>> for Error { |
479 | #[inline] |
480 | - fn from(kind: Box<dyn std::error::Error + Sync + Send + 'static>) -> Self { |
481 | - Self::new(kind.to_string()).set_source(Some(kind.into())) |
482 | + fn from(err: Box<dyn error::Error + Sync + Send + 'static>) -> Self { |
483 | + Self::from_inner(err.into()) |
484 | } |
485 | } |
486 | |
487 | impl From<std::ffi::NulError> for Error { |
488 | #[inline] |
489 | - fn from(kind: std::ffi::NulError) -> Self { |
490 | - Self::new(kind.to_string()).set_source(Some(Arc::new(kind))) |
491 | + fn from(err: std::ffi::NulError) -> Self { |
492 | + Self::from_inner(Arc::new(err)).set_kind(ErrorKind::Bug) |
493 | } |
494 | } |
495 | |
496 | impl From<nix::Error> for Error { |
497 | #[inline] |
498 | - fn from(kind: nix::Error) -> Self { |
499 | - Self::new(kind.to_string()).set_source(Some(Arc::new(kind))) |
500 | + fn from(err: nix::Error) -> Self { |
501 | + Self::from_inner(Arc::new(err)).set_kind(ErrorKind::Platform) |
502 | } |
503 | } |
504 | |
505 | #[cfg(feature = "sqlite3")] |
506 | impl From<rusqlite::Error> for Error { |
507 | #[inline] |
508 | - fn from(kind: rusqlite::Error) -> Self { |
509 | - Self::new(kind.to_string()).set_source(Some(Arc::new(kind))) |
510 | + fn from(err: rusqlite::Error) -> Self { |
511 | + Self::from_inner(Arc::new(err)).set_kind(ErrorKind::LinkedLibrary("sqlite3")) |
512 | } |
513 | } |
514 | |
515 | @@ -512,80 +563,76 @@ impl From<notify::Error> for Error { |
516 | let kind = match err.kind { |
517 | notify::ErrorKind::MaxFilesWatch |
518 | | notify::ErrorKind::WatchNotFound |
519 | - | notify::ErrorKind::Generic(_) => ErrorKind::External, |
520 | + | notify::ErrorKind::Generic(_) => ErrorKind::Platform, |
521 | notify::ErrorKind::Io(_) => ErrorKind::OSError, |
522 | notify::ErrorKind::PathNotFound => ErrorKind::Configuration, |
523 | notify::ErrorKind::InvalidConfig(_) => ErrorKind::Bug, |
524 | }; |
525 | - Self::new(err.to_string()) |
526 | - .set_source(Some(Arc::new(err))) |
527 | - .set_kind(kind) |
528 | + Self::from_inner(Arc::new(err)).set_kind(kind) |
529 | } |
530 | } |
531 | |
532 | impl From<libloading::Error> for Error { |
533 | #[inline] |
534 | - fn from(kind: libloading::Error) -> Self { |
535 | - Self::new(kind.to_string()).set_source(Some(Arc::new(kind))) |
536 | + fn from(err: libloading::Error) -> Self { |
537 | + Self::from_inner(Arc::new(err)) |
538 | } |
539 | } |
540 | |
541 | impl From<&str> for Error { |
542 | #[inline] |
543 | - fn from(kind: &str) -> Self { |
544 | - Self::new(kind.to_string()) |
545 | + fn from(err: &str) -> Self { |
546 | + Self::new(err.to_string()) |
547 | } |
548 | } |
549 | |
550 | impl From<String> for Error { |
551 | #[inline] |
552 | - fn from(kind: String) -> Self { |
553 | - Self::new(kind) |
554 | + fn from(err: String) -> Self { |
555 | + Self::new(err) |
556 | } |
557 | } |
558 | |
559 | impl From<nom::Err<(&[u8], nom::error::ErrorKind)>> for Error { |
560 | #[inline] |
561 | - fn from(kind: nom::Err<(&[u8], nom::error::ErrorKind)>) -> Self { |
562 | - Self::new("Parsing error").set_source(Some(Arc::new(Self::new(kind.to_string())))) |
563 | + fn from(err: nom::Err<(&[u8], nom::error::ErrorKind)>) -> Self { |
564 | + Self::new("Parsing error").set_details(err.to_string()) |
565 | } |
566 | } |
567 | |
568 | impl From<nom::Err<(&str, nom::error::ErrorKind)>> for Error { |
569 | #[inline] |
570 | - fn from(kind: nom::Err<(&str, nom::error::ErrorKind)>) -> Self { |
571 | - Self::new("Parsing error").set_details(kind.to_string()) |
572 | + fn from(err: nom::Err<(&str, nom::error::ErrorKind)>) -> Self { |
573 | + Self::new("Parsing error").set_details(err.to_string()) |
574 | } |
575 | } |
576 | |
577 | impl From<crate::email::InvalidHeaderName> for Error { |
578 | #[inline] |
579 | - fn from(kind: crate::email::InvalidHeaderName) -> Self { |
580 | - Self::new(kind.to_string()) |
581 | - .set_source(Some(Arc::new(kind))) |
582 | - .set_kind(ErrorKind::Network(NetworkErrorKind::InvalidTLSConnection)) |
583 | + fn from(err: crate::email::InvalidHeaderName) -> Self { |
584 | + Self::from_inner(Arc::new(err)).set_kind(ErrorKind::ValueError) |
585 | } |
586 | } |
587 | |
588 | impl<'a> From<&'a mut Self> for Error { |
589 | #[inline] |
590 | - fn from(kind: &'a mut Self) -> Self { |
591 | - kind.clone() |
592 | + fn from(err: &'a mut Self) -> Self { |
593 | + err.clone() |
594 | } |
595 | } |
596 | |
597 | impl<'a> From<&'a Self> for Error { |
598 | #[inline] |
599 | - fn from(kind: &'a Self) -> Self { |
600 | - kind.clone() |
601 | + fn from(err: &'a Self) -> Self { |
602 | + err.clone() |
603 | } |
604 | } |
605 | |
606 | impl From<base64::DecodeError> for Error { |
607 | #[inline] |
608 | - fn from(kind: base64::DecodeError) -> Self { |
609 | - Self::new("base64 decoding failed") |
610 | - .set_source(Some(Arc::new(kind))) |
611 | + fn from(err: base64::DecodeError) -> Self { |
612 | + Self::from_inner(Arc::new(err)) |
613 | + .set_summary("base64 decoding failed") |
614 | .set_kind(ErrorKind::ValueError) |
615 | } |
616 | } |
617 | diff --git a/melib/src/imap/error.rs b/melib/src/imap/error.rs |
618 | index 599a9e2..7cf1362 100644 |
619 | --- a/melib/src/imap/error.rs |
620 | +++ b/melib/src/imap/error.rs |
621 | @@ -33,12 +33,14 @@ impl From<ValidationError> for Error { |
622 | #[inline] |
623 | fn from(error: ValidationError) -> Self { |
624 | Self { |
625 | - summary: error.to_string().into(), |
626 | + summary: "IMAP transaction validation failed".into(), |
627 | details: None, |
628 | - source: Some(Arc::new(error)), |
629 | + inner: None, |
630 | + source: None, |
631 | related_path: None, |
632 | kind: ErrorKind::Bug, |
633 | } |
634 | + .set_source(Some(Arc::new(error))) |
635 | } |
636 | } |
637 | |
638 | @@ -49,12 +51,14 @@ where |
639 | #[inline] |
640 | fn from(error: AppendError<S, L>) -> Self { |
641 | Self { |
642 | - summary: error.to_string().into(), |
643 | + summary: "IMAP APPEND command failed".into(), |
644 | details: None, |
645 | - source: Some(Arc::new(error)), |
646 | + inner: None, |
647 | + source: None, |
648 | related_path: None, |
649 | kind: ErrorKind::Bug, |
650 | } |
651 | + .set_source(Some(Arc::new(error))) |
652 | } |
653 | } |
654 | |
655 | @@ -65,12 +69,14 @@ where |
656 | #[inline] |
657 | fn from(error: CopyError<S, L>) -> Self { |
658 | Self { |
659 | - summary: error.to_string().into(), |
660 | + summary: "IMAP COPY command failed".into(), |
661 | details: None, |
662 | - source: Some(Arc::new(error)), |
663 | + inner: None, |
664 | + source: None, |
665 | related_path: None, |
666 | kind: ErrorKind::Bug, |
667 | } |
668 | + .set_source(Some(Arc::new(error))) |
669 | } |
670 | } |
671 | |
672 | @@ -81,12 +87,14 @@ where |
673 | #[inline] |
674 | fn from(error: MoveError<S, M>) -> Self { |
675 | Self { |
676 | - summary: error.to_string().into(), |
677 | + summary: "IMAP MOVE command failed".into(), |
678 | + source: None, |
679 | details: None, |
680 | - source: Some(Arc::new(error)), |
681 | + inner: None, |
682 | related_path: None, |
683 | kind: ErrorKind::Bug, |
684 | } |
685 | + .set_source(Some(Arc::new(error))) |
686 | } |
687 | } |
688 | |
689 | @@ -97,11 +105,13 @@ where |
690 | #[inline] |
691 | fn from(error: ListError<L1, L2>) -> Self { |
692 | Self { |
693 | - summary: error.to_string().into(), |
694 | + summary: "IMAP LIST command failed".into(), |
695 | details: None, |
696 | - source: Some(Arc::new(error)), |
697 | + inner: None, |
698 | + source: None, |
699 | related_path: None, |
700 | kind: ErrorKind::Bug, |
701 | } |
702 | + .set_source(Some(Arc::new(error))) |
703 | } |
704 | } |
705 | diff --git a/melib/src/utils/tests.rs b/melib/src/utils/tests.rs |
706 | index 4c3170f..90f1e95 100644 |
707 | --- a/melib/src/utils/tests.rs |
708 | +++ b/melib/src/utils/tests.rs |
709 | @@ -21,6 +21,7 @@ |
710 | // SPDX-License-Identifier: EUPL-1.2 OR GPL-3.0-or-later |
711 | |
712 | #[test] |
713 | + #[ignore] |
714 | fn test_shellexpandtrait() { |
715 | use std::{fs::File, io::Write, os::unix::fs::PermissionsExt, path::Path}; |
716 | |
717 | @@ -144,6 +145,7 @@ fn test_shellexpandtrait() { |
718 | |
719 | #[cfg(target_os = "linux")] |
720 | #[test] |
721 | + #[ignore] |
722 | fn test_shellexpandtrait_impls() { |
723 | use std::{fs::File, io::Write, os::unix::fs::PermissionsExt, path::Path}; |
724 |