diff --git a/build.rs b/build.rs index 73618336..91fa9098 100644 --- a/build.rs +++ b/build.rs @@ -23,7 +23,7 @@ fn main() { subplot_build::codegen("sq.subplot") .expect("failed to generate code with Subplot"); - let mut sq = cli::build(); + let mut sq = cli::build(false); generate_shell_completions(&mut sq).unwrap(); generate_man_pages(&sq).unwrap(); } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 1ce9c98d..9545fb53 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -125,7 +125,12 @@ pub const THIRD_PARTY_CERTIFICATION_VALIDITY_DURATION: Duration = Duration::new( pub const GLOBAL_OPTIONS_HEADER: &str = "Global Options"; -pub fn build() -> Command { +/// Builds the top-level Clap command. +/// +/// If `globals_hidden` is true, then top-level, global arguments are +/// marked as hidden, which means they won't be displayed in the help +/// output. +pub fn build(globals_hidden: bool) -> Command { let sq_version = Box::leak( format!( "{} (sequoia-openpgp {}, using {})", @@ -135,7 +140,20 @@ pub fn build() -> Command { ) .into_boxed_str(), ) as &str; - SqCommand::command().version(sq_version) + + let mut command = SqCommand::command().version(sq_version); + + // Change the globals to be hidden. + if globals_hidden { + command = command.mut_args(|mut a| { + if a.is_global_set() { + a = a.hide(globals_hidden); + } + a + }); + }; + + command } /// Defines the CLI. diff --git a/src/sq.rs b/src/sq.rs index 14cfe99c..55c638e9 100644 --- a/src/sq.rs +++ b/src/sq.rs @@ -1053,7 +1053,57 @@ impl<'store> Config<'store> { // TODO: Use `derive`d command structs. No more values_of // TODO: Handling (and cli position) of global arguments fn main() -> Result<()> { - let c = cli::SqCommand::from_arg_matches(&cli::build().get_matches())?; + let mut cli = cli::build(true); + let matches = cli.clone().try_get_matches(); + let c = match matches { + Ok(matches) => { + cli::SqCommand::from_arg_matches(&matches)? + } + Err(err) => { + // Warning: hack ahead! + // + // If we are showing the help output, we only want to + // display the global options at the top-level; for + // subcommands we hide the global options to not overwhelm + // the user. + // + // Ideally, clap would provide a mechanism to only show + // the help output for global options at the level they + // are defined at. That's not the case. + // + // We can use `err` to figure out if we are showing the + // help output, but it doesn't tell us what subcommand we + // are showing the help for. Instead (and here's the + // hack!), we compare the output. If it is the output for + // the top-level `--help` or `-h`, then we are showing the + // help for the top-level. If not, then we are showing + // the help for a subcommand. In the former case, we + // unhide the global options. + use clap::error::ErrorKind; + if err.kind() == ErrorKind::DisplayHelp + || err.kind() == ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand + { + let output = err.render(); + let output = if output == cli.render_long_help() { + Some(cli::build(false).render_long_help()) + } else if output == cli.render_help() { + Some(cli::build(false).render_help()) + } else { + None + }; + + if let Some(output) = output { + if err.use_stderr() { + eprint!("{}", output); + } else { + print!("{}", output); + } + std::process::exit(err.exit_code()); + } + } + err.exit(); + } + }; let time: SystemTime = c.time.clone().unwrap_or_else(|| Time::now()).into();