Expand description

Command chaining: setup.py sdist bdist

Unlike click, bpaf allows commands to be nested as well for as long as the paring is not ambiguous

Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
    switch: bool,
    commands: Vec<Cmd>,
}

#[derive(Debug, Clone)]
enum Cmd {
    Eat(String),
    Drink(bool),
    Sleep(usize),
}

fn cmd() -> impl Parser<Cmd> {
    let eat = positional::<String>("FOOD")
        .to_options()
        .command("eat")
        .adjacent()
        .map(Cmd::Eat);

    let drink = long("coffee")
        .switch()
        .to_options()
        .command("drink")
        .adjacent()
        .map(Cmd::Drink);

    let sleep = long("time")
        .argument::<usize>("HOURS")
        .to_options()
        .command("sleep")
        .adjacent()
        .map(Cmd::Sleep);

    construct!([eat, drink, sleep])
}

pub fn options() -> OptionParser<Options> {
    let switch = short('s').switch();
    let commands = cmd().many();
    construct!(Options { commands, switch }).to_options()
}
Examples

You can chain one or more commands, commands can be arbitrarily nested too

% app eat fastfood drink --coffee sleep --time=5
Options { switch: false, commands: [Eat("fastfood"), Drink(true), Sleep(5)] }

You can pass other flags after all the commands but not in between them since commands are treated as positionals. It should be possible to consume items before and between commands as well if they are consumed before the commands like this: construct!(Options { switch, commands }) but in that case you need to be careful about not consuming anything from the command themselves.

% app sleep --time 10 eat "Bak Kut Teh" drink -s
Options { switch: true, commands: [Sleep(10), Eat("Bak Kut Teh"), Drink(false)] }