From c3cc40d2ac6f93b5e16177c274861b8f9c860e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Lapastora?= Date: Sun, 6 Feb 2022 23:04:28 +0100 Subject: [PATCH] feat(cli): Print arguments if argument parsing fails (#3560) * fix(#3554): Print the command line argv on clap error This is a very bare implementation that just prints the error and then a note with the arguments passed, it does this manually and doesn't use clap. I've also chosen to use `Vec`'s `Debug` implementation instead of rolling my own one because I thought it was good enough, but there might be a better way of doing all this. Altogether, I think this will be very useful to help in the diagnostic of other bugs :) * fix(#3554): Print the command line argv on clap error This is a very bare implementation that just prints the error and then a note with the arguments passed, it does this manually and doesn't use clap. I've also chosen to use `Vec`'s `Debug` implementation instead of rolling my own one because I thought it was good enough, but there might be a better way of doing all this. Altogether, I think this will be very useful to help in the diagnostic of other bugs :) EDIT: removed `dbg!`, set it to exit always. * correctness(exit): don't print argv / exit with error on help and version error kinds * fix: Avoid panicking when stdout/stderr closing unexpectedly * refactor(cli): use `use_stderr` instead of manual match for error kinds `clap` uses `use_stderr` to reliably check whether the error given is actually an error coming from user input or rather a hint to display other info (version, help, etc.) Also reworded/moved a couple of comments so that they explain better what is the thought process behind the code --- src/main.rs | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index c41926d4..a2dfad57 100644 --- a/src/main.rs +++ b/src/main.rs @@ -108,7 +108,38 @@ fn main() { let _ = ansi_term::enable_ansi_support(); logger::init(); - let args = Cli::parse(); + let args = match Cli::try_parse() { + Ok(args) => args, + Err(e) => { + // if the error is not printed to stderr, this means it was not really + // an error but rather some information is going to be listed, therefore + // we won't print the arguments passed + let is_info_only = !e.use_stderr(); + // print the error and void panicking in case of stdout/stderr closing unexpectedly + let _ = e.print(); + // if there was no mistake by the user and we're only going to display information, + // we won't put arguments or exit with non-zero code + let exit_code = if is_info_only { + 0 + } else { + // print the arguments + // avoid panicking in case of stderr closing + let mut stderr = io::stderr(); + use io::Write; + let _ = writeln!( + stderr, + "\nNOTE:\n passed arguments: {:?}", + // collect into a vec to format args as a slice + std::env::args().skip(1).collect::>() + ); + // clap exits with status 2 on error: + // https://docs.rs/clap/latest/clap/struct.Error.html#method.exit + 2 + }; + + std::process::exit(exit_code); + } + }; log::trace!("Parsed arguments: {:#?}", args); match args.command {