Skip to content

Commit bf3a4c5

Browse files
committed
error: add ErrorChainDisplay struct for better output
Add an ErrorChainDisplay struct that borrows an error and prints the list of chained errors one by one, showing relationships and metadata for each error. Signed-off-by: Manos Pitsidianakis <[email protected]>
1 parent 4b959f5 commit bf3a4c5

File tree

5 files changed

+202
-133
lines changed

5 files changed

+202
-133
lines changed

meli/src/mail/compose/hooks.rs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
//! Pre-submission hooks for draft validation and/or transformations.
2323
pub use std::borrow::Cow;
2424

25-
use melib::email::headers::HeaderName;
25+
use melib::{email::headers::HeaderName, src_err_arc_wrap};
2626

2727
use super::*;
2828

@@ -102,11 +102,11 @@ impl Hook {
102102
.stderr(Stdio::piped())
103103
.spawn()
104104
.map_err(|err| -> Error {
105-
format!(
105+
Error::new(format!(
106106
"could not execute `{command}`. Check if its binary is in PATH or if \
107-
the command is valid. Original error: {err}"
108-
)
109-
.into()
107+
the command is valid."
108+
))
109+
.set_source(Some(src_err_arc_wrap! {err}))
110110
})?;
111111
let mut stdin = child
112112
.stdin
@@ -121,7 +121,8 @@ impl Hook {
121121
});
122122
});
123123
let output = child.wait_with_output().map_err(|err| -> Error {
124-
format!("failed to wait on hook child {name_}: {err}").into()
124+
Error::new(format!("failed to wait on hook child {name_}"))
125+
.set_source(Some(src_err_arc_wrap! {err}))
125126
})?;
126127
let stdout = String::from_utf8_lossy(&output.stdout);
127128
let stderr = String::from_utf8_lossy(&output.stderr);
@@ -187,7 +188,10 @@ fn important_header_warn(_ctx: &mut Context, draft: &mut Draft) -> Result<()> {
187188
for hdr in [HeaderName::FROM, HeaderName::TO] {
188189
match draft.headers.get(&hdr).map(melib::Address::list_try_from) {
189190
Some(Ok(_)) => {}
190-
Some(Err(err)) => return Err(format!("{hdr} header value is invalid ({err}).").into()),
191+
Some(Err(err)) => {
192+
return Err(Error::new(format!("{hdr} header value is invalid"))
193+
.set_source(Some(src_err_arc_wrap! {err})))
194+
}
191195
None => return Err(format!("{hdr} header is missing and should be present.").into()),
192196
}
193197
}
@@ -198,8 +202,11 @@ fn important_header_warn(_ctx: &mut Context, draft: &mut Draft) -> Result<()> {
198202
.get(HeaderName::DATE)
199203
.map(melib::utils::datetime::rfc822_to_timestamp)
200204
{
201-
Some(Err(err)) => return Err(format!("Date header value is invalid ({err}).").into()),
202-
Some(Ok(0)) => return Err("Date header value is invalid.".into()),
205+
Some(Err(err)) => {
206+
return Err(Error::new("Date header value is invalid.")
207+
.set_source(Some(src_err_arc_wrap! {err})))
208+
}
209+
Some(Ok(0)) => return Err(Error::new("Date header value is invalid.")),
203210
_ => {}
204211
}
205212
}
@@ -211,7 +218,8 @@ fn important_header_warn(_ctx: &mut Context, draft: &mut Draft) -> Result<()> {
211218
.filter(|v| !v.trim().is_empty())
212219
.map(melib::Address::list_try_from)
213220
{
214-
return Err(format!("{hdr} header value is invalid ({err}).").into());
221+
return Err(Error::new(format!("{hdr} header value is invalid"))
222+
.set_source(Some(src_err_arc_wrap! {err})));
215223
}
216224
}
217225
Ok(())
@@ -310,8 +318,9 @@ mod tests {
310318
let err_msg = hook(&mut ctx, &mut draft).unwrap_err().to_string();
311319
assert_eq!(
312320
err_msg,
313-
"From header value is invalid (Parsing error. In input: \"...\",\nError: Alternative, \
314-
Many1, Alternative, atom(): starts with whitespace or empty).",
321+
"From header value is invalid\nCaused by:\n[2] Parsing error. In input: \
322+
\"...\",\nError: Alternative, Many1, Alternative, atom(): starts with whitespace or \
323+
empty",
315324
"HEADERWARN should complain about From value being empty: {}",
316325
err_msg
317326
);
@@ -321,8 +330,9 @@ mod tests {
321330
let err_msg = hook(&mut ctx, &mut draft).unwrap_err().to_string();
322331
assert_eq!(
323332
err_msg,
324-
"To header value is invalid (Parsing error. In input: \"...\",\nError: Alternative, \
325-
Many1, Alternative, atom(): starts with whitespace or empty).",
333+
"To header value is invalid\nCaused by:\n[2] Parsing error. In input: \
334+
\"...\",\nError: Alternative, Many1, Alternative, atom(): starts with whitespace or \
335+
empty",
326336
"HEADERWARN should complain about To value being empty: {}",
327337
err_msg
328338
);
@@ -350,8 +360,8 @@ mod tests {
350360
let err_msg = hook(&mut ctx, &mut draft).unwrap_err().to_string();
351361
assert_eq!(
352362
err_msg,
353-
"From header value is invalid (Parsing error. In input: \"user \
354-
[email protected]>...\",\nError: Alternative, Tag).",
363+
"From header value is invalid\nCaused by:\n[2] Parsing error. In input: \"user \
364+
[email protected]>...\",\nError: Alternative, Tag",
355365
"HEADERWARN should complain about From value being invalid: {}",
356366
err_msg
357367
);

meli/tests/test_cli_subcommands.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ server_password_command = "false"
223223
output
224224
.code(1)
225225
.stderr(predicate::eq(
226-
"Edit the sample configuration and relaunch meli.\nKind: Configuration\n",
226+
"Configuration error: Edit the sample configuration and relaunch meli.\n",
227227
))
228228
.stdout(
229229
predicate::eq(

0 commit comments

Comments
 (0)