pub struct NamedArg { /* private fields */ }
Expand description

A named thing used to create flag, switch or argument

Named items (argument, flag and switch) can have up to 2 visible names (one short and one long) and multiple hidden short and long aliases if needed. It’s also possible to consume items from environment variables using env. You usually start with short or long function, then apply short / long / env / help repeatedly to build a desired set of names then transform it into a parser using flag, switch or positional.

Derive usage

Unlike combinatoric API where you forced to specify names for your parsers derive API allows to omit some or all the details:

  1. If no naming information is present at all - bpaf_derive would use field name as a long name (or a short name if field name consists of a single character)

  2. If short or long annotation is present without an argument - bpaf_derive would use first character or a full name as long and short name respectively. It won’t try to add implicit long or short name from the previous item.

  3. If short or long annotation is present with an argument - those are values bpaf_derive would use instead of the original field name

  4. If env(arg) annotation is present - bpaf_derive would generate .env(arg) method:

    const DB: &str = "top_secret_database";
    
    #[derive(Debug, Clone, Bpaf)]
    #[bpaf(options)]
    pub struct Config {
       /// flag with no annotation
       pub flag_1: bool,
    
       /// explicit short suppresses long
       #[bpaf(short)]
       pub flag_2: bool,
    
       /// explicit short with custom letter
       #[bpaf(short('z'))]
       pub flag_3: bool,
    
       /// explicit short and long
       #[bpaf(short, long)]
       pub deposit: bool,
    
       /// implicit long + env variable from DB constant
       #[bpaf(env(DB))]
       pub database: String,
    
       /// implicit long + env variable "USER"
       #[bpaf(env("USER"))]
       pub user: String,
    }

Example

$ app --help
   <skip>
        --flag-1         flag with no annotation
   -f                    explicit short suppresses long
   -z                    explicit short with custom letter
   -d, --deposit         explicit short and long
       --database <ARG>  [env:top_secret_database: N/A]
                         implicit long + env variable from DB constant
       --user <ARG>      [env:USER = "pacak"]
                         implicit long + env variable "USER"
   <skip>

Implementations

Add a short name to a flag/switch/argument

Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
    switch: bool,
    arg: usize,
    username: String,
}

pub fn options() -> OptionParser<Options> {
    let switch = short('s') // first `short` creates a builder
        .short('S') // second switch is a hidden alias
        .long("switch") // visible long name
        .long("also-switch") // hidden alias
        .help("Switch with many names")
        .switch(); // `switch` finalizes the builder

    let arg = long("argument") // long is also a builder
        .short('a')
        .short('A')
        .long("also-arg")
        .help("Argument with names")
        .argument::<usize>("ARG");

    let username = long("user")
        .short('u')
        .env("USER1")
        .help("Custom user name")
        .argument::<String>("USER");

    construct!(Options {
        switch,
        arg,
        username
    })
    .to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    #[bpaf(short, long, short('S'), long("also-switch"))]
    /// Switch with many names
    switch: bool,
    #[bpaf(short, long("argument"), short('A'), long("also-arg"))]
    /// Argument with names
    arg: usize,
    #[bpaf(short, long("user"), env("USER1"), argument("USER"))]
    /// Custom user name
    username: String,
}
Examples

As usual switch is optional, arguments are required

% app -a 42 -u Bobert
Options { switch: false, arg: 42, username: "Bobert" }

Help displays only visible aliases (and a current value for env arguments)

% app --help
Usage: [-s] -a ARG -u USER

Available options:
    -s, --switch          Switch with many names
    -a, --argument <ARG>  Argument with names
    -u, --user <USER>     [env:USER1 = "pacak"]
                          Custom user name
    -h, --help            Prints help information

But you can still use hidden aliases, both short and long

% app --also-switch --also-arg 330 --user Bobert
Options { switch: true, arg: 330, username: "Bobert" }

And unless there’s many or similar modifiers having multiple aliases doesn’t mean you can specify them multiple times:

% app -A 42 -a 330 -u Bobert
-a is not expected in this context

Also hidden aliases are really hidden and only meant to do backward compatibility stuff, they won’t show up anywhere else in completions or error messages

% app -a 42 -A 330 -u Bobert
No such flag: `-A`, did you mean `-u`?
Examples found in repository?
examples/enum_in_args.rs (line 31)
29
30
31
32
33
34
35
36
37
38
fn main() {
    let opt = long("baz")
        .short('b')
        .help("choose between foo, bar or foobar")
        .argument::<Baz>("CMD")
        .to_options()
        .run();

    println!("{:#?}", opt);
}

Add a long name to a flag/switch/argument

Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
    switch: bool,
    arg: usize,
    username: String,
}

pub fn options() -> OptionParser<Options> {
    let switch = short('s') // first `short` creates a builder
        .short('S') // second switch is a hidden alias
        .long("switch") // visible long name
        .long("also-switch") // hidden alias
        .help("Switch with many names")
        .switch(); // `switch` finalizes the builder

    let arg = long("argument") // long is also a builder
        .short('a')
        .short('A')
        .long("also-arg")
        .help("Argument with names")
        .argument::<usize>("ARG");

    let username = long("user")
        .short('u')
        .env("USER1")
        .help("Custom user name")
        .argument::<String>("USER");

    construct!(Options {
        switch,
        arg,
        username
    })
    .to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    #[bpaf(short, long, short('S'), long("also-switch"))]
    /// Switch with many names
    switch: bool,
    #[bpaf(short, long("argument"), short('A'), long("also-arg"))]
    /// Argument with names
    arg: usize,
    #[bpaf(short, long("user"), env("USER1"), argument("USER"))]
    /// Custom user name
    username: String,
}
Examples

As usual switch is optional, arguments are required

% app -a 42 -u Bobert
Options { switch: false, arg: 42, username: "Bobert" }

Help displays only visible aliases (and a current value for env arguments)

% app --help
Usage: [-s] -a ARG -u USER

Available options:
    -s, --switch          Switch with many names
    -a, --argument <ARG>  Argument with names
    -u, --user <USER>     [env:USER1 = "pacak"]
                          Custom user name
    -h, --help            Prints help information

But you can still use hidden aliases, both short and long

% app --also-switch --also-arg 330 --user Bobert
Options { switch: true, arg: 330, username: "Bobert" }

And unless there’s many or similar modifiers having multiple aliases doesn’t mean you can specify them multiple times:

% app -A 42 -a 330 -u Bobert
-a is not expected in this context

Also hidden aliases are really hidden and only meant to do backward compatibility stuff, they won’t show up anywhere else in completions or error messages

% app -a 42 -A 330 -u Bobert
No such flag: `-A`, did you mean `-u`?
Examples found in repository?
examples/top_to_bottom.rs (line 35)
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
76
77
fn debug() -> impl Parser<bool> {
    short('d')
        .long("debug")
        .help("Activate debug mode")
        .switch()
}
// number of occurrences of the v/verbose flag capped at 3
fn verbose() -> impl Parser<usize> {
    short('v')
        .long("verbose")
        .help("Increase the verbosity\nYou can specify it up to 3 times\neither as -v -v -v or as -vvv")
        .req_flag(())
        .many()
        .map(|xs| xs.len())
        .guard(|&x| x <= 3, "It doesn't get any more verbose than this")
}

// an argument, parsed and with default value
fn speed() -> impl Parser<f64> {
    short('s')
        .long("speed")
        .help("Set speed")
        .argument::<f64>("SPEED")
        .fallback(42.0)
}

fn output() -> impl Parser<PathBuf> {
    short('o')
        .long("output")
        .help("output file")
        .argument::<PathBuf>("OUTPUT")
}

// no magical name transmogrifications.
fn nb_cars() -> impl Parser<u32> {
    short('n').long("nb-cars").argument::<u32>("N")
}

fn files_to_process() -> impl Parser<Vec<PathBuf>> {
    short('f')
        .long("file")
        .help("File to process")
        .argument::<PathBuf>("FILE")
        .many()
}
More examples
Hide additional examples
examples/derive_show_asm.rs (line 44)
42
43
44
45
46
47
48
49
fn verbosity() -> impl Parser<usize> {
    short('v')
        .long("verbose")
        .help("more verbose output, can be specified multiple times")
        .req_flag(())
        .many()
        .map(|v| v.len())
}
examples/derive.rs (line 30)
27
28
29
30
31
32
33
34
35
36
fn verbose() -> impl Parser<usize> {
    // number of occurrences of the v/verbose flag capped at 3
    short('v')
        .long("verbose")
        .help("Increase the verbosity\nYou can specify it up to 3 times\neither as -v -v -v or as -vvv")
        .req_flag(())
        .many()
        .map(|xs| xs.len())
        .guard(|&x| x <= 3, "It doesn't get any more verbose than this")
}
examples/enum_tuple.rs (line 17)
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/xorg.rs (line 60)
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
pub fn options() -> OptionParser<Options> {
    let backing = toggle_options("backing", "Backing status").fallback(false);
    let xinerama = toggle_options("xinerama", "Xinerama status").fallback(true);
    let turbo = short('t')
        .long("turbo")
        .help("Engage the turbo mode")
        .switch();
    let extensions = extension().many();
    construct!(Options {
        turbo,
        backing,
        xinerama,
        extensions,
    })
    .to_options()
}
examples/no_import.rs (line 13)
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
fn main() {
    // A flag, true if used in the command line. Can be required, this one is optional
    let debug = bpaf::short('d')
        .long("debug")
        .help("Activate debug mode")
        .switch();

    // an argument, parsed and with default value
    let speed = bpaf::Parser::fallback(
        bpaf::short('s')
            .long("speed")
            .help("Set speed")
            .argument::<f64>("SPEED"),
        42.0,
    );

    // packing things in a struct assumes parser for each field is in scope.
    let opt = bpaf::Parser::to_options(bpaf::construct!(Out { debug, speed })).run();

    println!("{:#?}", opt);
}

Environment variable fallback

If named value isn’t present - try to fallback to this environment variable.

You can specify it multiple times, bpaf would use items past the first one as hidden aliases.

For flag and switch environment variable being present gives the same result as the flag being present, allowing to implement things like NO_COLOR variables:

$ NO_COLOR=1 app --do-something
Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
    switch: bool,
    arg: usize,
    username: String,
}

pub fn options() -> OptionParser<Options> {
    let switch = short('s') // first `short` creates a builder
        .short('S') // second switch is a hidden alias
        .long("switch") // visible long name
        .long("also-switch") // hidden alias
        .help("Switch with many names")
        .switch(); // `switch` finalizes the builder

    let arg = long("argument") // long is also a builder
        .short('a')
        .short('A')
        .long("also-arg")
        .help("Argument with names")
        .argument::<usize>("ARG");

    let username = long("user")
        .short('u')
        .env("USER1")
        .help("Custom user name")
        .argument::<String>("USER");

    construct!(Options {
        switch,
        arg,
        username
    })
    .to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    #[bpaf(short, long, short('S'), long("also-switch"))]
    /// Switch with many names
    switch: bool,
    #[bpaf(short, long("argument"), short('A'), long("also-arg"))]
    /// Argument with names
    arg: usize,
    #[bpaf(short, long("user"), env("USER1"), argument("USER"))]
    /// Custom user name
    username: String,
}
Examples

As usual switch is optional, arguments are required

% app -a 42 -u Bobert
Options { switch: false, arg: 42, username: "Bobert" }

Help displays only visible aliases (and a current value for env arguments)

% app --help
Usage: [-s] -a ARG -u USER

Available options:
    -s, --switch          Switch with many names
    -a, --argument <ARG>  Argument with names
    -u, --user <USER>     [env:USER1 = "pacak"]
                          Custom user name
    -h, --help            Prints help information

But you can still use hidden aliases, both short and long

% app --also-switch --also-arg 330 --user Bobert
Options { switch: true, arg: 330, username: "Bobert" }

And unless there’s many or similar modifiers having multiple aliases doesn’t mean you can specify them multiple times:

% app -A 42 -a 330 -u Bobert
-a is not expected in this context

Also hidden aliases are really hidden and only meant to do backward compatibility stuff, they won’t show up anywhere else in completions or error messages

% app -a 42 -A 330 -u Bobert
No such flag: `-A`, did you mean `-u`?
Examples found in repository?
examples/env_variable.rs (line 13)
11
12
13
14
15
16
17
18
19
20
pub fn main() {
    let key = long("key")
        .env("ACCESS_KEY")
        .help("access key to use")
        .argument::<String>("KEY");

    let opts = construct!(Opts { key }).to_options().run();

    println!("{:?}", opts);
}
More examples
Hide additional examples
examples/multiple_fallback.rs (line 27)
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
pub fn main() {
    let field1 = long("field1")
        .env("FIELD1")
        .help("Field 1")
        .argument::<u32>("ARG")
        .fallback(DEFAULT_CONFIG.field1);
    let field2 = long("field2")
        .env("FIELD2")
        .help("Field 2")
        .argument::<u64>("ARG")
        .fallback(DEFAULT_CONFIG.field2);

    let opts = construct!(Config { field1, field2 }).to_options().run();

    // At this point if you get opts - it should be taken from one of
    // - the command line argument
    // - the environmental variable
    // - the config file
    // - the hard-coded default (from config parser)
    println!("{:?}", opts);
}

Add a help message to a flag/switch/argument

Combinatoric usage
fn parse_bool() -> impl Parser<bool> {
    short('f')
        .long("flag")
        .help("a flag that does a thing")
        .switch()
}
Derive usage

bpaf_derive converts doc comments into option help by following those rules:

  1. It skips blank lines, if present.
  2. It stops parsing after a double blank line.
#[derive(Debug, Clone, Bpaf)]
struct Options {
    /// This line is part of help message
    ///
    /// So is this one
    ///
    ///
    /// But this one isn't
    key: String,
}

See NamedArg for more details

Examples found in repository?
examples/top_to_bottom.rs (line 36)
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
76
77
fn debug() -> impl Parser<bool> {
    short('d')
        .long("debug")
        .help("Activate debug mode")
        .switch()
}
// number of occurrences of the v/verbose flag capped at 3
fn verbose() -> impl Parser<usize> {
    short('v')
        .long("verbose")
        .help("Increase the verbosity\nYou can specify it up to 3 times\neither as -v -v -v or as -vvv")
        .req_flag(())
        .many()
        .map(|xs| xs.len())
        .guard(|&x| x <= 3, "It doesn't get any more verbose than this")
}

// an argument, parsed and with default value
fn speed() -> impl Parser<f64> {
    short('s')
        .long("speed")
        .help("Set speed")
        .argument::<f64>("SPEED")
        .fallback(42.0)
}

fn output() -> impl Parser<PathBuf> {
    short('o')
        .long("output")
        .help("output file")
        .argument::<PathBuf>("OUTPUT")
}

// no magical name transmogrifications.
fn nb_cars() -> impl Parser<u32> {
    short('n').long("nb-cars").argument::<u32>("N")
}

fn files_to_process() -> impl Parser<Vec<PathBuf>> {
    short('f')
        .long("file")
        .help("File to process")
        .argument::<PathBuf>("FILE")
        .many()
}
More examples
Hide additional examples
examples/enum_in_args.rs (line 32)
29
30
31
32
33
34
35
36
37
38
fn main() {
    let opt = long("baz")
        .short('b')
        .help("choose between foo, bar or foobar")
        .argument::<Baz>("CMD")
        .to_options()
        .run();

    println!("{:#?}", opt);
}
examples/derive_show_asm.rs (line 45)
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
fn verbosity() -> impl Parser<usize> {
    short('v')
        .long("verbose")
        .help("more verbose output, can be specified multiple times")
        .req_flag(())
        .many()
        .map(|v| v.len())
}

fn parse_manifest_path() -> impl Parser<PathBuf> {
    long("manifest-path")
        .help("Path to Cargo.toml")
        .argument::<PathBuf>("PATH")
        .parse(|p| {
            if p.is_absolute() {
                Ok(p)
            } else {
                std::env::current_dir()
                    .map(|d| d.join(p))
                    .and_then(|full_path| full_path.canonicalize())
            }
        })
        .fallback_with(|| std::env::current_dir().map(|x| x.join("Cargo.toml")))
}

#[derive(Debug, Clone, Bpaf)]
pub struct Format {
    /// Print interleaved Rust code
    pub rust: bool,

    #[bpaf(external(color_detection))]
    pub color: bool,

    /// include full demangled name instead of just prefix
    pub full_name: bool,
}

#[derive(Debug, Clone, Bpaf)]
pub enum Syntax {
    /// Generate assembly using Intel style
    Intel,
    /// Generate assembly using AT&T style
    Att,
}

impl ToString for Syntax {
    fn to_string(&self) -> String {
        match self {
            Syntax::Intel => String::from("llvm-args=-x86-asm-syntax=intel"),
            Syntax::Att => String::from("llvm-args=-x86-asm-syntax=att"),
        }
    }
}

fn color_detection() -> impl Parser<bool> {
    let yes = long("color")
        .help("Enable color highlighting")
        .req_flag(true);
    let no = long("no-color")
        .help("Disable color highlighting")
        .req_flag(false);
    construct!([yes, no]).fallback_with::<_, &str>(|| Ok(true))
}
examples/env_variable.rs (line 14)
11
12
13
14
15
16
17
18
19
20
pub fn main() {
    let key = long("key")
        .env("ACCESS_KEY")
        .help("access key to use")
        .argument::<String>("KEY");

    let opts = construct!(Opts { key }).to_options().run();

    println!("{:?}", opts);
}
examples/ex_positional.rs (line 14)
12
13
14
15
16
17
18
19
20
21
fn main() {
    let value = long("value")
        .help("Mysterious value")
        .argument::<u32>("VAL")
        .fallback(42);
    let files = positional::<PathBuf>("FILE").many();
    let opts = construct!(Options { value, files }).to_options().run();

    println!("{:#?}", opts);
}
examples/env_logger.rs (line 30)
28
29
30
31
32
33
34
35
36
37
38
39
fn verbose() -> impl Parser<LevelFilter> {
    short('v')
        .help("Verbosity level, use multiple times for more verbosity")
        .req_flag(())
        .many()
        .map(|v| {
            use LevelFilter::*;
            *[Off, Error, Warn, Info, Debug, Trace]
                .get(v.len())
                .unwrap_or(&Trace)
        })
}

Simple boolean flag

A special case of a flag that gets decoded into a bool, mostly serves as a convenient shortcut to .flag(true, false).

Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
    decision: bool,
}

fn parse_decision() -> impl Parser<bool> {
    long("decision").help("Positive decision").switch()
}

pub fn options() -> OptionParser<Options> {
    let decision = parse_decision();
    construct!(Options { decision }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    decision: bool,
}
Examples

Presense of a long name is decoded into true

% app --decision
Options { decision: true }

Absense is false

% app 
Options { decision: false }
See [`NamedArg`] for more details
Examples found in repository?
examples/top_to_bottom.rs (line 37)
33
34
35
36
37
38
fn debug() -> impl Parser<bool> {
    short('d')
        .long("debug")
        .help("Activate debug mode")
        .switch()
}
More examples
Hide additional examples
examples/dd.rs (line 50)
49
50
51
52
53
54
55
56
57
58
pub fn options() -> OptionParser<Options> {
    let magic = long("magic").switch();
    construct!(Options {
        magic,
        in_file(),
        out_file(),
        block_size(),
    })
    .to_options()
}
examples/customize_help.rs (line 9)
6
7
8
9
10
11
12
13
14
15
16
17
18
fn main() {
    let opt = short('d')
        .help("Release the dragon")
        .switch()
        .to_options()
        .descr("I am a program and I do things")
        .header("Sometimes they even work.")
        .footer("Beware `-d`, dragons be here")
        .usage("You can call it with following flags: {usage}")
        .run();

    println!("{:?}", opt);
}
examples/flatten.rs (line 25)
24
25
26
27
28
29
30
31
32
33
34
35
36
fn main() {
    let verbose = short('v').help("switch verbosity on").switch();
    let user = short('u').help("daemon user").argument::<String>("USER");
    let group = short('g').help("daemon group").argument::<String>("GROUP");
    let daemon_opts = construct!(DaemonOpts { user, group });
    let opt = construct!(Cmdline {
        verbose,
        daemon_opts
    })
    .to_options()
    .run();
    println!("{:?}", opt);
}
examples/xorg.rs (line 62)
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
pub fn options() -> OptionParser<Options> {
    let backing = toggle_options("backing", "Backing status").fallback(false);
    let xinerama = toggle_options("xinerama", "Xinerama status").fallback(true);
    let turbo = short('t')
        .long("turbo")
        .help("Engage the turbo mode")
        .switch();
    let extensions = extension().many();
    construct!(Options {
        turbo,
        backing,
        xinerama,
        extensions,
    })
    .to_options()
}
examples/no_import.rs (line 15)
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
fn main() {
    // A flag, true if used in the command line. Can be required, this one is optional
    let debug = bpaf::short('d')
        .long("debug")
        .help("Activate debug mode")
        .switch();

    // an argument, parsed and with default value
    let speed = bpaf::Parser::fallback(
        bpaf::short('s')
            .long("speed")
            .help("Set speed")
            .argument::<f64>("SPEED"),
        42.0,
    );

    // packing things in a struct assumes parser for each field is in scope.
    let opt = bpaf::Parser::to_options(bpaf::construct!(Out { debug, speed })).run();

    println!("{:#?}", opt);
}

Flag with custom present/absent values

More generic version of switch that uses arbitrary type instead of bool.

Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
    decision: Decision,
}

#[derive(Debug, Clone)]
pub enum Decision {
    Yes,
    No,
}

fn parse_decision() -> impl Parser<Decision> {
    long("decision")
        .help("Positive decision")
        .flag(Decision::Yes, Decision::No)
}

pub fn options() -> OptionParser<Options> {
    let decision = parse_decision();
    construct!(Options { decision }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    #[bpaf(flag(Decision::Yes, Decision::No))]
    decision: Decision,
}

#[derive(Debug, Clone)]
pub enum Decision {
    Yes,
    No,
}
Examples

Presense of a long name is decoded into Yes

% app --decision
Options { decision: Yes }

Absense is No

% app 
Options { decision: No }
Examples found in repository?
examples/verbose.rs (line 40)
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
fn main() {
    // program takes one or more -v or --verbose flags, more flags = higher verbosity.
    // parser handles number and produces a single flag.
    //
    // let's create it without using any single purpose magical functions

    // Let's staty by creating a simple parser that handles a single -v / --verbose
    // and fails otherwise;
    let verbose = short('v').long("verbose").req_flag(());

    // .many() tries to appy parser as many times as possible and collects the results.
    // We can't use non failing parse with .many() since it will loop forever.
    let verbose = verbose.many();

    // Then count how many times parser succeeded
    let verbose = verbose.map(|v| v.len());

    // And add a simple sanity checker.
    // By this time when this parser succeeds - it will contain verbosity in 0..3 range, inclusive.
    let verbose = verbose.guard(|&x| x <= 3, "it doesn't get any more verbose than 3");

    // program takes --trimor --no-trimflag, but not both at once. If none is given -
    // fallback value is to disable trimming. Trim enum is set accordingly

    // this flag succeeds iff --no-trim is given and produces Trim::Off
    let trim_off = long("no-trim").req_flag(Trim::Off);

    // this flag handles two remaining cases: --trim is given (Trim::On) an fallback (Trim::Off)
    let trim_on = long("trim").flag(Trim::On, Trim::Off);

    // combination of previous two.
    // if trim_off succeeds - trim_on never runs, otherwise trim_on tries to handle the remaining
    // case before falling back to Trim:Off.
    // If both --trim and --no-trim are given trim_off succeeds, trim_off never runs and --trim
    // remains unused - parser fails
    let trim = construct!([trim_off, trim_on]);

    let parser = construct!(verbose, trim);

    let opt = parser.to_options().run();
    println!("{:#?}", opt);
}

Required flag with custom value

Similar to flag takes no option arguments, but would only succeed if user specifies it on a command line. Not very useful by itself and works best in combination with other parsers.

Using req_flag to implement 3-state options.

In derive mode bpaf would transform field-less enum variants into req_flag In addition to naming annotations (short, long and env) such variants also accepts hide and default annotations. Former hides it from --help (see hide, later makes it a default choice if preceeding variants fail to parse. You shoud only use default annotation on the last variant of enum. To better convey the meaning you might want to use a combination of skip and fallback annotations, see examples.

Additionally bpaf_derive handles () fields as req_flag see adjacent for more details. See NamedArg for more details

Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
    decision: Decision,
}

#[derive(Debug, Clone)]
pub enum Decision {
    On,
    Off,
    Undecided,
}

// user can specify either --on or --off, parser would fallback to `Undecided`
fn parse_decision() -> impl Parser<Decision> {
    let on = long("on").help("Positive decision").req_flag(Decision::On);
    let off = long("off")
        .help("Negative decision")
        .req_flag(Decision::Off);
    construct!([on, off]).fallback(Decision::Undecided)
}

pub fn options() -> OptionParser<Options> {
    let decision = parse_decision();
    construct!(Options { decision }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    #[bpaf(external)]
    decision: Decision,
}

#[derive(Debug, Clone, Bpaf)]
#[bpaf(fallback(Decision::Undecided))]
pub enum Decision {
    /// Positive decision
    On,
    /// Negative decision
    Off,
    #[bpaf(skip)]
    Undecided,
}
Examples

This example implements a tri-state switch: wether decision was made positive, negative or not at all. Alternative implementation can use optional and None to indicate “no decision” case. A case with positive decision:

% app --on
Options { decision: On }

A case with no decision:

% app 
Options { decision: Undecided }

--on and --off are mutually exclusive:

% app --on --off
--off is not expected in this context

help

% app --help
Usage: [--on | --off]

Available options:
        --on    Positive decision
        --off   Negative decision
    -h, --help  Prints help information
Examples found in repository?
examples/at_least_two.rs (line 9)
7
8
9
10
11
12
13
14
15
16
fn main() {
    let opt = short('f')
        .req_flag(())
        .many()
        .guard(|x| x.len() >= 2, "at least two arguments are required")
        .to_options()
        .run();

    println!("{:?}", opt);
}
More examples
Hide additional examples
examples/derive_show_asm.rs (line 46)
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
fn verbosity() -> impl Parser<usize> {
    short('v')
        .long("verbose")
        .help("more verbose output, can be specified multiple times")
        .req_flag(())
        .many()
        .map(|v| v.len())
}

fn parse_manifest_path() -> impl Parser<PathBuf> {
    long("manifest-path")
        .help("Path to Cargo.toml")
        .argument::<PathBuf>("PATH")
        .parse(|p| {
            if p.is_absolute() {
                Ok(p)
            } else {
                std::env::current_dir()
                    .map(|d| d.join(p))
                    .and_then(|full_path| full_path.canonicalize())
            }
        })
        .fallback_with(|| std::env::current_dir().map(|x| x.join("Cargo.toml")))
}

#[derive(Debug, Clone, Bpaf)]
pub struct Format {
    /// Print interleaved Rust code
    pub rust: bool,

    #[bpaf(external(color_detection))]
    pub color: bool,

    /// include full demangled name instead of just prefix
    pub full_name: bool,
}

#[derive(Debug, Clone, Bpaf)]
pub enum Syntax {
    /// Generate assembly using Intel style
    Intel,
    /// Generate assembly using AT&T style
    Att,
}

impl ToString for Syntax {
    fn to_string(&self) -> String {
        match self {
            Syntax::Intel => String::from("llvm-args=-x86-asm-syntax=intel"),
            Syntax::Att => String::from("llvm-args=-x86-asm-syntax=att"),
        }
    }
}

fn color_detection() -> impl Parser<bool> {
    let yes = long("color")
        .help("Enable color highlighting")
        .req_flag(true);
    let no = long("no-color")
        .help("Disable color highlighting")
        .req_flag(false);
    construct!([yes, no]).fallback_with::<_, &str>(|| Ok(true))
}
examples/top_to_bottom.rs (line 44)
40
41
42
43
44
45
46
47
48
fn verbose() -> impl Parser<usize> {
    short('v')
        .long("verbose")
        .help("Increase the verbosity\nYou can specify it up to 3 times\neither as -v -v -v or as -vvv")
        .req_flag(())
        .many()
        .map(|xs| xs.len())
        .guard(|&x| x <= 3, "It doesn't get any more verbose than this")
}
examples/env_logger.rs (line 31)
28
29
30
31
32
33
34
35
36
37
38
39
fn verbose() -> impl Parser<LevelFilter> {
    short('v')
        .help("Verbosity level, use multiple times for more verbosity")
        .req_flag(())
        .many()
        .map(|v| {
            use LevelFilter::*;
            *[Off, Error, Warn, Info, Debug, Trace]
                .get(v.len())
                .unwrap_or(&Trace)
        })
}
examples/derive.rs (line 32)
27
28
29
30
31
32
33
34
35
36
fn verbose() -> impl Parser<usize> {
    // number of occurrences of the v/verbose flag capped at 3
    short('v')
        .long("verbose")
        .help("Increase the verbosity\nYou can specify it up to 3 times\neither as -v -v -v or as -vvv")
        .req_flag(())
        .many()
        .map(|xs| xs.len())
        .guard(|&x| x <= 3, "It doesn't get any more verbose than this")
}
examples/sensors.rs (line 35)
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
fn opts() -> Opts {
    let sensor = long("sensor").req_flag(());
    let device = long("sensor-device").argument::<String>("DEVICE");
    let name = long("sensor-name").argument::<String>("NAME");

    // from_str needs to be replaced with `parse` that can deal with hex digits
    let bus_id = long("sensor-i2c-bus").argument::<usize>("BUS");
    let address = long("sensor-i2c-address").argument::<usize>("ADDRESS");
    let sensors = construct!(Sensor {
        sensor,
        device,
        name,
        bus_id,
        address
    })
    .adjacent()
    .many();
    construct!(Opts { sensors }).to_options().run()
}

Argument

A short (-a) or long (--name) name followed by either a space or = and then by a string literal. -f foo, --flag bar or -o=- are all valid argument examples. Note, string literal can’t start with - unless separated from the flag with =. For short flags value can follow immediately: -fbar.

When using combinatoring API you must specify the type with turbofish, for parsing types that don’t implement FromOsStr you can use FromUtf8 helper tag.

fn parse_arg() -> impl Parser<usize> {
    short('a').argument::<usize>("ARG")
}
Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
    value: isize,
    shorty: u64,
}

pub fn options() -> OptionParser<Options> {
    let value = long("value").argument::<isize>("ARG").fallback(100);
    // You can use FromUtf8 type tag to parse things that only implement `FromStr`, but not `FromOsStr`
    // `u64` implements both and only used as an example
    let shorty = short('s').argument::<FromUtf8<u64>>("ARG");
    construct!(Options { value, shorty }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    #[bpaf(fallback(100))]
    value: isize,
    // You can use FromUtf8 type tag to parse things that only implement FromStr, but not FromOsStr
    // `u64` implements both and only used as an example
    #[bpaf(short, argument::<FromUtf8<u64>>("ARG"))]
    shorty: u64,
}
Examples

Names for arguments could be short or long, and they can be parsed as mutiple different types, this example uses isize and u64

% app --value 50 -s=18446744073709551615
Options { value: 50, shorty: 18446744073709551615 }

Value can be separated from the flag by space, = or for short ones - be immediately adjacent

% app --value=1 -s42
Options { value: 1, shorty: 42 }

You can apply fallback and other transformation

% app -s0
Options { value: 100, shorty: 0 }

But if there’s no fallback - the value is required

% app --value 1
Expected -s ARG, pass --help for usage information

Argument is required

% app -s
-s requires an argument
Examples found in repository?
examples/top_to_bottom.rs (line 55)
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
76
77
fn speed() -> impl Parser<f64> {
    short('s')
        .long("speed")
        .help("Set speed")
        .argument::<f64>("SPEED")
        .fallback(42.0)
}

fn output() -> impl Parser<PathBuf> {
    short('o')
        .long("output")
        .help("output file")
        .argument::<PathBuf>("OUTPUT")
}

// no magical name transmogrifications.
fn nb_cars() -> impl Parser<u32> {
    short('n').long("nb-cars").argument::<u32>("N")
}

fn files_to_process() -> impl Parser<Vec<PathBuf>> {
    short('f')
        .long("file")
        .help("File to process")
        .argument::<PathBuf>("FILE")
        .many()
}
More examples
Hide additional examples
examples/enum_in_args.rs (line 33)
29
30
31
32
33
34
35
36
37
38
fn main() {
    let opt = long("baz")
        .short('b')
        .help("choose between foo, bar or foobar")
        .argument::<Baz>("CMD")
        .to_options()
        .run();

    println!("{:#?}", opt);
}
examples/env_variable.rs (line 15)
11
12
13
14
15
16
17
18
19
20
pub fn main() {
    let key = long("key")
        .env("ACCESS_KEY")
        .help("access key to use")
        .argument::<String>("KEY");

    let opts = construct!(Opts { key }).to_options().run();

    println!("{:?}", opts);
}
examples/ex_positional.rs (line 15)
12
13
14
15
16
17
18
19
20
21
fn main() {
    let value = long("value")
        .help("Mysterious value")
        .argument::<u32>("VAL")
        .fallback(42);
    let files = positional::<PathBuf>("FILE").many();
    let opts = construct!(Options { value, files }).to_options().run();

    println!("{:#?}", opts);
}
examples/flatten.rs (line 26)
24
25
26
27
28
29
30
31
32
33
34
35
36
fn main() {
    let verbose = short('v').help("switch verbosity on").switch();
    let user = short('u').help("daemon user").argument::<String>("USER");
    let group = short('g').help("daemon group").argument::<String>("GROUP");
    let daemon_opts = construct!(DaemonOpts { user, group });
    let opt = construct!(Cmdline {
        verbose,
        daemon_opts
    })
    .to_options()
    .run();
    println!("{:?}", opt);
}
examples/enum_tuple.rs (line 19)
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);
}

Trait Implementations

Returns a copy of the value. Read more
Performs copy-assignment from source. Read more
Formats the value using the given formatter. Read more

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The resulting type after obtaining ownership.
Creates owned data from borrowed data, usually by cloning. Read more
Uses borrowed data to replace owned data, usually by cloning. Read more
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.