pub fn command<T>(
name: &'static str,
subparser: OptionParser<T>
) -> ParseCommand<T>where
T: 'static,
Expand description
Subcommand parser
Subcommands allow to use a totally independent parser inside a current one. Inner parser can have its own help message, description, version and so on. You can nest them arbitrarily too.
Alternatively you can create commands using command
Important restriction
When parsing command arguments from command lines you should have parsers for all your
named values and command before parsers for positional items. In derive API fields parsed as
positional should be at the end of your struct
/enum
. Same rule applies
to parsers with positional fields or commands inside: such parsers should go to the end as well.
Use check_invariants
in your test to ensure correctness.
For example for non positional non_pos
and a command command
parsers
let valid = construct!(non_pos(), command());
let invalid = construct!(command(), non_pos());
bpaf
panics during help generation unless if this restriction holds
Combinatoric usage
#[derive(Debug, Clone)]
pub struct Cmd {
flag: bool,
arg: usize,
}
#[derive(Debug, Clone)]
pub struct Options {
flag: bool,
cmd: Cmd,
}
fn cmd() -> impl Parser<Cmd> {
let flag = long("flag")
.help("This flag is specific to command")
.switch();
let arg = long("arg").argument::<usize>("ARG");
construct!(Cmd { flag, arg })
.to_options()
.descr("Command to do something")
.command("cmd")
.help("Command to do something")
}
pub fn options() -> OptionParser<Options> {
let flag = long("flag")
.help("This flag is specific to the outer layer")
.switch();
construct!(Options { flag, cmd() }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(command)]
/// Command to do something
pub struct Cmd {
/// This flag is specific to command
flag: bool,
arg: usize,
}
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
/// This flag is specific to the outer layer
flag: bool,
#[bpaf(external)]
cmd: Cmd,
}
Examples
In this example there’s only one command and it is required, so is the argument inside of it
% app cmd --arg 42
Options { flag: false, cmd: Cmd { flag: false, arg: 42 } }
If you don’t specify this command - parsing will fail
% app
Expected COMMAND ..., pass --help for usage information
You can have the same flag names inside and outside of the command, but it might be confusing for the end user. This example enables the outer flag
% app --flag cmd --arg 42
Options { flag: true, cmd: Cmd { flag: false, arg: 42 } }
And this one - both inside and outside
% app --flag cmd --arg 42 --flag
Options { flag: true, cmd: Cmd { flag: true, arg: 42 } }
And that’s the confusing part - unless you add context restrictions with
adjacent
and parse command first - outer flag wins.
So it’s best not to mix names on different levels
% app cmd --arg 42 --flag
Options { flag: true, cmd: Cmd { flag: false, arg: 42 } }
Commands show up on both outer level help
% app --help
Usage: [--flag] COMMAND ...
Available options:
--flag This flag is specific to the outer layer
-h, --help Prints help information
Available commands:
cmd Command to do something
As well as showing their own help
% app cmd --help
Command to do something
Usage: [--flag] --arg ARG
Available options:
--flag This flag is specific to command
--arg <ARG>
-h, --help Prints help information
Examples found in repository?
More examples
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
fn main() {
let bar = short('b')
.long("bar")
.help("some bar command")
.argument::<String>("BAR")
.optional();
let bar_cmd = construct!(Foo { bar })
.to_options()
.descr("This command will try to do foo given a bar argument");
let opt = command("foo", bar_cmd)
.help("command for doing foo")
.map(Command::Foo)
.to_options()
.run();
println!("{:#?}", opt);
}
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
fn main() {
let dry_run = long("dry_run").switch();
let all = long("all").switch();
let repository = positional::<String>("SRC").fallback("origin".to_string());
let fetch = construct!(Opt::Fetch {
dry_run,
all,
repository
})
.to_options()
.descr("fetches branches from remote repository");
let fetch_cmd = command("fetch", fetch);
let interactive = short('i').switch();
let all = long("all").switch();
let files = positional::<PathBuf>("FILE").many();
let add = construct!(Opt::Add {
interactive,
all,
files
})
.to_options()
.descr("add files to the staging area");
let add_cmd = command("add", add).help("add files to the staging area");
let opt = construct!([fetch_cmd, add_cmd])
.to_options()
.descr("The stupid content tracker")
.run();
println!("{:?}", opt);
}
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
fn main() {
let token = long("token")
.help("Token used for complex commands")
.argument::<String>("TOKEN")
.optional();
// start with defining 3 commands: simple, complex1 and complex2
let simple_parser = pure(PreCommand::Simple).to_options();
let simple = command("simple", simple_parser);
let complex1_parser = positional::<i32>("ARG");
let complex1 = command(
"complex1",
construct!(PreCommand::Complex1(complex1_parser))
.to_options()
.descr("This is complex command 1"),
);
let complex2_parser = positional::<i16>("ARG");
let complex2 = command(
"complex1",
construct!(PreCommand::Complex2(complex2_parser))
.to_options()
.descr("This is complex command 2"),
);
// compose then to accept any of those
let preparser = construct!([simple, complex1, complex2]);
// make a parser that accepts optional token and one of incomplete commands
// then create complete command or fail
let parser = construct!(token, preparser).parse(|(token, cmd)| match cmd {
PreCommand::Simple => Ok(Command::Simple),
PreCommand::Complex1(a) => match token {
Some(token) => Ok(Command::Complex1(token, a)),
None => Err("You must specify token to use with --token"),
},
PreCommand::Complex2(a) => match token {
Some(token) => Ok(Command::Complex2(token, a)),
None => Err("You must specify token to use with --token"),
},
});
let cmd = parser.to_options().run();
println!("{:?}", cmd);
}