[−][src]Crate clap_nested
Convenient clap
for CLI apps with multi-level subcommands
clap-nested
provides a convenient way for setting up CLI apps
with multi-level subcommands.
We all know that clap
really shines when it comes to
parsing CLI arguments. It even supports nicely formatted help messages,
subcommands, and shell completion out of the box.
However, clap
is very much unopinionated in how we should
structure and execute logic. Even when we have tens of subcommands
(and arguments!), we still have to manually match against
all possible options and handle them accordingly. That process quickly
becomes tedious and unorganized.
So, clap-nested
add a little sauce of opinion into clap
to help with that.
Use case: Easy subcommands and command execution
In clap-nested
, commands are defined together with how to execute them.
Making it that way instead of going through a separate
matching-and-executing block of code like in clap
,
it's very natural to separate commands into different files
in an organized and structured way.
#[macro_use] extern crate clap; use clap::{Arg, ArgMatches}; use clap_nested::{Command, Commander}; fn main() { let foo = Command::new("foo") .description("Shows foo") .options(|app| { app.arg( Arg::with_name("debug") .short("d") .help("Prints debug information verbosely"), ) }) // Putting argument types here for clarity .runner(|args: &str, matches: &ArgMatches<'_>| { let debug = clap::value_t!(matches, "debug", bool).unwrap_or_default(); println!("Running foo, env = {}, debug = {}", args, debug); Ok(()) }); let bar = Command::new("bar") .description("Shows bar") // Putting argument types here for clarity .runner(|args: &str, _matches: &ArgMatches<'_>| { println!("Running bar, env = {}", args); Ok(()) }); Commander::new() .options(|app| { app.arg( Arg::with_name("environment") .short("e") .long("env") .global(true) .takes_value(true) .value_name("STRING") .help("Sets an environment value, defaults to \"dev\""), ) }) // `Commander::args()` derives arguments to pass to subcommands. // Notice all subcommands (i.e. `foo` and `bar`) will accept `&str` as arguments. .args(|_args, matches| matches.value_of("environment").unwrap_or("dev")) // Add all subcommands .add_cmd(foo) .add_cmd(bar) // To handle when no subcommands match .no_cmd(|_args, _matches| { println!("No subcommand matched"); Ok(()) }) .run(); }
Use case: Straightforward multi-level subcommands
Commander
acts like a runnable group
of subcommands, calling run
on a Commander
gets the whole execution process started.
On the other hand, Commander
could also be converted into a MultiCommand
to be further included (and executed)
under another Commander
.
This makes writing multi-level subcommands way easy.
use clap_nested::{Commander, MultiCommand}; let multi_cmd: MultiCommand<(), ()> = Commander::new() // Add some theoretical subcommands // .add_cmd(model) // .add_cmd(controller) // Specify a name for the newly converted command .into_cmd("generate") // Optionally specify a description .description("Generates resources");
Use case: Printing help messages directly on errors
clap
is also the CLI parsing library which powers Cargo.
Sometimes when you run a Cargo command wrongly, you may see this:
$ cargo run -x
error: Found argument '-x' which wasn't expected, or isn't valid in this context
USAGE:
cargo run [OPTIONS] [--] [args]...
For more information try --help
While it works and is better for separation of concern (one command, one job, no suprise effect), we often wish for more. We want the help message to be printed directly on errors, so it doesn't take us one more command to show the help message (and then maybe one more to run the supposedly correct command).
That's why we take a bit of trade-off to change the default behavior
of clap
. It now works this way:
$ cargo run -x
error: Found argument '-x' which wasn't expected, or isn't valid in this context
cargo-run
Run a binary or example of the local package
USAGE:
cargo run [OPTIONS] [--] [args]...
OPTIONS:
-q, --quiet No output printed to stdout
--bin <NAME>... Name of the bin target to run
--example <NAME>... Name of the example target to run
-p, --package <SPEC> Package with the target to run
-j, --jobs <N> Number of parallel jobs, defaults to # of CPUs
<...omitted for brevity...>
ARGS:
<args>...
<...omitted for brevity...>
Macros
file_stem | Get filename without extension of the current source file |
Structs
Command | Define a single-purpose command to be included
in a |
Commander | Define a group of subcommands to be run directly, or converted as a whole into a higher-order command |
MultiCommand | The result of converting a |