diff --git a/.Jules/palette.md b/.Jules/palette.md index 7a9147b..7ff298e 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -1,3 +1,14 @@ ## 2024-07-11 - Route UI print methods to stderr **Learning:** Routing standard informational/success/error/warning messages to `stderr` allows users to pipe CLI data from `stdout` cleanly without mixing it with UI feedback. This is a common pattern in Unix tools. **Action:** Ensure `ui.print_*` methods use `eprintln!` instead of `println!`. +## 2024-07-18 - Better nested errors with anyhow's chain + +**Learning:** anyhow's default `Debug` format displays errors as a raw numbered list like: +``` +Caused by: + 0: Failed to send login request + 1: client error +``` +Which is not very pleasing to the eye or easy to parse for users. + +**Action:** Instead of returning `anyhow::Result<()>` from `main()`, we can return `std::process::ExitCode`. This allows us to manually iterate through `anyhow::Error::chain().enumerate()`, displaying the root cause in bold, and cleanly indenting nested causes with a `↳` symbol. diff --git a/src/main.rs b/src/main.rs index 0bdd357..573f463 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,28 @@ -use anyhow::Result; use clap::Parser; +use console::style; use kaji::args::Cli; +use kaji::utils::ui::ERROR; #[cfg(not(tarpaulin_include))] #[tokio::main] -async fn main() -> Result<()> { +async fn main() -> std::process::ExitCode { dotenvy::dotenv().ok(); dotenvy::from_filename(".secrets").ok(); env_logger::init(); let cli = Cli::parse(); - kaji::run_app(cli).await + if let Err(err) = kaji::run_app(cli).await { + eprintln!("{} {}", ERROR, style("Error:").red().bold()); + for (i, cause) in err.chain().enumerate() { + if i == 0 { + eprintln!(" {}", style(cause).bold()); + } else { + eprintln!(" {} {}", style("↳").dim(), cause); + } + } + std::process::ExitCode::FAILURE + } else { + std::process::ExitCode::SUCCESS + } }