1use crate::error::RajacError;
2use crate::result::RajacResult;
3use std::fmt::Write as _;
4use std::process::ExitCode;
5
6pub fn try_main(run: impl FnOnce() -> RajacResult<()>) -> ExitCode {
11 match run() {
12 Ok(()) => ExitCode::SUCCESS,
13 Err(error) => {
14 eprint!("{}", format_cli_error("operation failed", &error));
15 ExitCode::FAILURE
16 }
17 }
18}
19
20pub fn try_main_with_headline(headline: &str, run: impl FnOnce() -> RajacResult<()>) -> ExitCode {
25 match run() {
26 Ok(()) => ExitCode::SUCCESS,
27 Err(error) => {
28 eprint!("{}", format_cli_error(headline, &error));
29 ExitCode::FAILURE
30 }
31 }
32}
33
34pub fn format_cli_error(headline: &str, error: &RajacError) -> String {
39 let mut rendered = String::new();
40 let _ = writeln!(&mut rendered, "\u{1b}[1;31m━━ {}\u{1b}[0m", headline);
41 error
42 .write_to(&mut rendered)
43 .expect("error rendering should not fail");
44
45 let mut causes = Vec::new();
46 let mut current = error.source();
47 while let Some(cause) = current {
48 causes.push((cause.kind().to_string(), cause.location()));
49 current = cause.source();
50 }
51
52 if !causes.is_empty() {
53 rendered.push('\n');
54 rendered.push_str("\u{1b}[1;33m━━ cause chain\u{1b}[0m\n");
55 for (cause, location) in &causes {
56 let _ = writeln!(&mut rendered, " • {}", cause);
57 let _ = writeln!(
58 &mut rendered,
59 " at {}:{}:{}",
60 location.file(),
61 location.line(),
62 location.column()
63 );
64 }
65 }
66
67 rendered
68}
69
70#[cfg(test)]
71mod tests {
72 use crate::error::RajacError;
73 use expect_test::expect;
74
75 use super::format_cli_error;
76
77 #[test]
78 fn format_cli_error_renders_headline_and_cause_chain() {
79 let error = RajacError::message("failed to verify")
80 .with_source(RajacError::message("missing reference output"));
81
82 expect!([r#"
83 ━━ verification failed
84 × error failed to verify
85 at crates/base/src/cli.rs:79:21
86 caused by: missing reference output
87 at crates/base/src/cli.rs:80:26
88
89 ━━ cause chain
90 • missing reference output
91 at crates/base/src/cli.rs:80:26
92 "#])
93 .assert_eq(&crate::unansi(&format_cli_error(
94 "verification failed",
95 &error,
96 )));
97 }
98}