aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorDaniel Mueller <deso@posteo.net>2020-10-04 09:46:31 -0700
committerDaniel Mueller <deso@posteo.net>2020-10-11 17:07:08 -0700
commitf65b150049ec9dc5ff2500d7e54c464d530f2e66 (patch)
treebd63618507dee5ec142c606662356a61fd8c1d00 /src/main.rs
parent8cf63c6790192c30c81294e7a940d470bf061cbf (diff)
downloadnitrocli-f65b150049ec9dc5ff2500d7e54c464d530f2e66.tar.gz
nitrocli-f65b150049ec9dc5ff2500d7e54c464d530f2e66.tar.bz2
Display available extensions in the help text
With recent changes we are able to execute user-provided extensions through the program. However, discoverability is arguably lacking, because nitrocli provides no insight into what extensions are available to begin with. This patch changes this state of affairs by listing available extensions in the help text.
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs43
1 files changed, 38 insertions, 5 deletions
diff --git a/src/main.rs b/src/main.rs
index e7a7d2f..c8b73dc 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -69,6 +69,10 @@ use std::io;
use std::process;
use std::str;
+use structopt::clap::ErrorKind;
+use structopt::clap::SubCommand;
+use structopt::StructOpt;
+
const NITROCLI_BINARY: &str = "NITROCLI_BINARY";
const NITROCLI_MODEL: &str = "NITROCLI_MODEL";
const NITROCLI_USB_PATH: &str = "NITROCLI_USB_PATH";
@@ -105,15 +109,44 @@ impl fmt::Display for DirectExitError {
impl error::Error for DirectExitError {}
/// Parse the command-line arguments and execute the selected command.
-fn handle_arguments(ctx: &mut Context<'_>, args: Vec<String>) -> anyhow::Result<()> {
- use structopt::StructOpt;
-
- match args::Args::from_iter_safe(args.iter()) {
+fn handle_arguments(ctx: &mut Context<'_>, argv: Vec<String>) -> anyhow::Result<()> {
+ match args::Args::from_iter_safe(argv.iter()) {
Ok(args) => {
ctx.config.update(&args);
args.cmd.execute(ctx)
}
- Err(err) => {
+ Err(mut err) => {
+ if err.kind == ErrorKind::HelpDisplayed {
+ // For the convenience of the user we'd like to list the
+ // available extensions in the help text. At the same time, we
+ // don't want to unconditionally iterate through PATH (which may
+ // contain directories with loads of files that need scanning)
+ // for every command invoked. So we do that listing only if a
+ // help text is actually displayed.
+ let path = ctx.path.clone().unwrap_or_else(ffi::OsString::new);
+ if let Ok(extensions) = commands::discover_extensions(&path) {
+ let mut clap = args::Args::clap();
+ for name in extensions {
+ // Because of clap's brain dead API, we see no other way
+ // but to leak the string we created here. That's okay,
+ // though, because we exit in a moment anyway.
+ let about = Box::leak(format!("Run the {} extension", name).into_boxed_str());
+ clap = clap.subcommand(
+ SubCommand::with_name(&name)
+ // Use some magic number here that causes all
+ // extensions to be listed after all other
+ // subcommands.
+ .display_order(1000)
+ .about(about as &'static str),
+ );
+ }
+ // At this point we are *pretty* sure that repeated invocation
+ // will result in another error. So should be fine to unwrap
+ // here.
+ err = clap.get_matches_from_safe(argv.iter()).unwrap_err();
+ }
+ }
+
if err.use_stderr() {
Err(err.into())
} else {