Function bpaf::command

source ·
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?
src/info.rs (line 618)
614
615
616
617
618
619
    pub fn command(self, name: &'static str) -> ParseCommand<T>
    where
        T: 'static,
    {
        crate::params::command(name, self)
    }
More examples
Hide additional examples
examples/enum_tuple.rs (line 26)
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);
}
examples/git.rs (line 38)
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);
}
examples/confusing.rs (line 38)
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);
}