Struct bpaf::parsers::NamedArg

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

A named thing used to create flag, switch or argument

§Combinatoric usage

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(Debug, Clone)]
pub enum Output {
    ToFile(PathBuf),
    ToConsole,
}
pub fn options() -> OptionParser<(usize, Output, bool)> {
    // In most cases you don't keep `NamedArg` around long enough
    // to assign it a name
    let size = short('s')
        .long("size")
        .help("Maximum size to process")
        .argument("SIZE");

    // but it can be useful if you want to have several arguments
    // sharing exact set of names - for example a switch (req_flag)
    // and an argument;
    let output = short('o').long("output");

    let to_file = output
        .clone()
        .help("Save output to file")
        .argument("PATH")
        .map(Output::ToFile);
    let to_console = output
        .help("Print output to console")
        .req_flag(Output::ToConsole);

    // when combining multiple parsers that can conflict with each other
    // it's a good idea to put more general first:
    let output = construct!([to_file, to_console]);

    let verbose = short('v')
        .long("verbose")
        .long("detailed")
        .help("Produce a detailed report")
        .switch();

    construct!(size, output, verbose).to_options()
}

fn main() {
    println!("{:?}", options().run())
}
Output

--help output will contain first short and first long names that are present and won’t have anything about hidden aliases.

$ app --help

Usage: app -s=SIZE (-o=PATH | -o) [-v]

Available options:
-s, --size=SIZE
Maximum size to process
-o, --output=PATH
Save output to file
-o, --output
Print output to console
-v, --verbose
Produce a detailed report
-h, --help
Prints help information

--detailed is a hidden alias and still works despite not being present in --help output above

$ app -o -s 2 --detailed
(2, ToConsole, true)

And hidden means actually hidden. While error message can suggest to fix a typo to make it a valid visible argument

$ app -o best.txt -s 10 --verbos
Error: no such flag: --verbos, did you mean --verbose?

It will not do so for hidden aliases

$ app -o best.txt -s 10 --detaile
Error: --detaile is not expected in this context

In this example names -o and --output can be parsed by two parsers - to_file and to_console, first one succeeds only if -o is followed by a non option name, best.txt.

$ app -o best.txt --size 10
(10, ToFile("best.txt"), false)

If such name is not present - parser will try to consume one without, producing ToConsole variant.

$ app -o -s 42
(42, ToConsole, false)

If neither is present - it fails - parser for output expects one of its branches to succeed

$ app -s 330
Error: expected --output=PATH or --output, pass --help for usage information

But this can be fixed with optional (not included in this example).

§Derive usage

When using derive API it is possible to omit some or all the details:

  1. If no naming information is present at all - bpaf 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 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 would use instead of the original field name
  4. You can specify many short and long names, any past the first one of each type will become hidden aliases
  5. If env(arg) annotation is present - in addition to long/short names derived according to rules 1..3 bpaf would also parse environment variable arg which can be a string literal or an expression.
const DB: &str = "DATABASE_VAR";

#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    /// Use verbose output
    // No name annotation and name is not a single character:
    // `bpaf` uses it as a long name - `--verbose`
    pub verbose: bool,

    /// Compile in a release mode
    #[bpaf(short)]
    // Name is long, but explicit annotation for a short name
    // `bpaf` makes a short name from the first symbol: `-r`
    pub release: bool,

    /// Number of parallel jobs, defaults to # of CPUs
    // Explicit annotation with a short name: `-j`
    #[bpaf(short('j'))]
    pub threads: Option<usize>,

    /// Upload artifacts to the storage
    // Explicit annotation for a single suppresses the oher one,
    // but you can specify both of them. `-u` and `--upload`
    #[bpaf(short, long)]
    pub upload: bool,

    /// List of features to activate
    // you can mix explicit annotations with and without names
    // when convenient, here it's `-F` and `--features`
    #[bpaf(short('F'), long)]
    pub features: Vec<String>,

    /// Read information from the database
    #[bpaf(env(DB))]
    // Annotation for `env` does not affect annotation for names
    // so `bpaf` makes `--database` flag too
    pub database: String,

    /// Only print essential information
    #[bpaf(short, long, long("essential"))]
    // `--essential` is a hidden ailias, `-q` and `--quiet` are visible
    pub quiet: bool,

    /// implicit long + env variable "USER"
    #[bpaf(env("USER"))]
    pub user: String,
}

fn main() {
    println!("{:?}", options().run())
}
Output

--help output will contain first short and first long names that are present and won’t have anything about hidden aliases.

$ app --help

Usage: app [--verbose] [-r] [-j=ARG] [-u] [-F=ARG]... --database=ARG [-q] --user=ARG

Available options:
--verbose
Use verbose output
-r
Compile in a release mode
-j=ARG
Number of parallel jobs, defaults to # of CPUs
-u, --upload
Upload artifacts to the storage
-F, --features=ARG
List of features to activate
--database=ARG
Read information from the database
[env:DATABASE_VAR: N/A]
-q, --quiet
Only print essential information
--user=ARG
implicit long + env variable "USER"
[env:USER = "pacak"]
-h, --help
Prints help information

--essential is a hidden alias and still works despite not being present in --help output above

$ app --database default --essential
Options { verbose: false, release: false, threads: None, upload: false, features: [], database: "default", quiet: true, user: "pacak" }

And hidden means actually hidden. While error message can suggest to fix a typo to make it a valid visible argument

$ app --database default --quie
Error: no such flag: --quie, did you mean --quiet?

It will not do so for hidden aliases

$ app --database default --essentia
Error: --essentia is not expected in this context

Implementations§

source§

impl NamedArg

source

pub fn short(self, short: char) -> Self

Add a short name to a flag/switch/argument

Combinatoric example
#[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()
}

fn main() {
    println!("{:?}", options().run())
}
Derive example
#[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,
}

fn main() {
    println!("{:?}", options().run())
}
Output

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: app [-s] -a=ARG -u=USER

Available options:
-s, --switch
Switch with many names
-a, --argument=ARG
Argument with names
-u, --user=USER
Custom user name
[env:USER1: N/A]
-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
Error: -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
Error: -A is not expected in this context
Examples found in repository?
examples/enum_in_args.rs (line 30)
28
29
30
31
32
33
34
35
36
37
fn main() {
    let opt = long("baz")
        .short('b')
        .help("choose between foo, bar or foobar")
        .argument::<Baz>("CMD")
        .to_options()
        .run();

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

pub fn long(self, long: &'static str) -> Self

Add a long name to a flag/switch/argument

Combinatoric example
#[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()
}

fn main() {
    println!("{:?}", options().run())
}
Derive example
#[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,
}

fn main() {
    println!("{:?}", options().run())
}
Output

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: app [-s] -a=ARG -u=USER

Available options:
-s, --switch
Switch with many names
-a, --argument=ARG
Argument with names
-u, --user=USER
Custom user name
[env:USER1: N/A]
-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
Error: -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
Error: -A is not expected in this context
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/dd.rs (line 53)
51
52
53
54
55
56
57
58
59
60
61
62
63
pub fn options() -> OptionParser<Options> {
    let magic = short('m')
        .long("magic")
        .help("a usual switch still works")
        .switch();
    construct!(Options {
        magic,
        in_file(),
        out_file(),
        block_size(),
    })
    .to_options()
}
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
34
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 = bar_cmd
        .command("foo")
        .help("command for doing foo")
        .map(Command::Foo)
        .to_options()
        .run();

    println!("{:#?}", opt);
}
examples/xorg.rs (line 46)
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
pub fn options() -> OptionParser<Options> {
    let backing = toggle_options("(+|-)backing", "backing", "Set backing status").fallback(false);
    let xinerama =
        toggle_options("(+|-)xinerama", "xinerama", "Set 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);
}
source

pub fn env(self, variable: &'static str) -> Self

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 example
#[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()
}

fn main() {
    println!("{:?}", options().run())
}
Derive example
#[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,
}

fn main() {
    println!("{:?}", options().run())
}
Output

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: app [-s] -a=ARG -u=USER

Available options:
-s, --switch
Switch with many names
-a, --argument=ARG
Argument with names
-u, --user=USER
Custom user name
[env:USER1: N/A]
-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
Error: -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
Error: -A is not expected in this context
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);
}
source

pub fn help<M>(self, help: M) -> Self
where M: Into<Doc>,

Add a help message to a flag/switch/argument

bpaf converts doc comments and string into help by following those rules:

  1. Everything up to the first blank line is included into a “short” help message
  2. Everything is included into a “long” help message
  3. bpaf preserves linebreaks followed by a line that starts with a space
  4. Linebreaks are removed otherwise

You can pass anything that can be converted into Doc, if you are not using documentation generation functionality (doc) this can be &str.

Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
    verbose: bool,
    name: String,
    output: Option<String>,
}

pub fn options() -> OptionParser<Options> {
    let verbose = short('v')
        .long("verbose")
        .help(
            "\
Output detailed help information, you can specify it multiple times

 when used once it outputs basic diagnostic info,
 when used twice or three times - it includes extra debugging.",
            // ^ note extra spaces before "when" that preserve the linebreaks
        )
        .switch();
    let name = long("name")
        .help("Use this as a task name")
        .argument("NAME");

    let output = positional("OUTPUT")
        .help("Save output to a file")
        .optional();

    construct!(Options {
        verbose,
        name,
        output
    })
    .to_options()
}

fn main() {
    println!("{:?}", options().run())
}
Derive example
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    #[bpaf(short, long)]
    /// Output detailed help information, you can specify it multiple times
    ///
    ///  when used once it outputs basic diagnostic info,
    ///  when used twice or three times - it includes extra debugging.
    //  ^ note extra spaces before when that preserve the linebreaks
    verbose: bool,

    #[bpaf(argument("NAME"))]
    /// Use this as a task name
    name: String,

    #[bpaf(positional("OUTPUT"))]
    /// Save output to a file
    output: Option<String>,
}

fn main() {
    println!("{:?}", options().run())
}
Output

When --help used once it renders shoter version of the help information

$ app --help

Usage: app [-v] --name=NAME [OUTPUT]

Available positional items:
OUTPUT
Save output to a file

Available options:
-v, --verbose
Output detailed help information, you can specify it multiple times
--name=NAME
Use this as a task name
-h, --help
Prints help information

When used twice - it renders full version. Documentation generator uses full version as well

$ app --help --help

Usage: app [-v] --name=NAME [OUTPUT]

Available positional items:
OUTPUT
Save output to a file

Available options:
-v, --verbose
Output detailed help information, you can specify it multiple times
when used once it outputs basic diagnostic info,
when used twice or three times - it includes extra debugging.
--name=NAME
Use this as a task name
-h, --help
Prints help information

Presence or absense of a help message should not affect the parser’s output

$ app --name Bob output.txt
Options { verbose: false, name: "Bob", output: Some("output.txt") }
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 31)
28
29
30
31
32
33
34
35
36
37
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 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/dynamic.rs (line 29)
26
27
28
29
30
31
32
fn string(name: &'static str) -> impl Parser<(String, Value)> {
    let label = name.to_string();
    long(name)
        .help("this can use a help message")
        .argument::<String>("NUM")
        .map(move |n| (label.clone(), Value::String(n)))
}
examples/dd.rs (line 54)
51
52
53
54
55
56
57
58
59
60
61
62
63
pub fn options() -> OptionParser<Options> {
    let magic = short('m')
        .long("magic")
        .help("a usual switch still works")
        .switch();
    construct!(Options {
        magic,
        in_file(),
        out_file(),
        block_size(),
    })
    .to_options()
}
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);
}
source

pub fn switch(self) -> ParseFlag<bool>

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).

In Derive API bpaf would use switch for bool fields inside named structs that don’t have other consumer annotations (flag, argument, etc).

Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
    verbose: bool,
    release: bool,
    default_features: bool,
}

pub fn options() -> OptionParser<Options> {
    let verbose = short('v')
        .long("verbose")
        .help("Produce verbose output")
        .switch();
    let release = long("release")
        .help("Build artifacts in release mode")
        .flag(true, false);
    let default_features = long("no-default-features")
        .help("Do not activate default features")
        // default_features uses opposite values,
        // producing `true` when value is absent
        .flag(false, true);

    construct!(Options {
        verbose,
        release,
        default_features,
    })
    .to_options()
}

fn main() {
    println!("{:?}", options().run())
}
Derive example
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    /// Produce verbose output
    // bpaf uses `switch` for `bool` fields in named
    // structs unless consumer attribute is present.
    // But it is also possible to give it explicit
    // consumer annotation to serve as a reminder:
    // #[bpaf(short, long, switch)]
    #[bpaf(short, long)]
    verbose: bool,

    #[bpaf(flag(true, false))]
    /// Build artifacts in release mode
    release: bool,

    /// Do not activate default features
    // default_features uses opposite values,
    // producing `true` when value is absent
    #[bpaf(long("no-default-features"), flag(false, true))]
    default_features: bool,
}

fn main() {
    println!("{:?}", options().run())
}
Output

In --help output bpaf shows switches as usual flags with no meta variable attached

$ app --help

Usage: app [-v] [--release] [--no-default-features]

Available options:
-v, --verbose
Produce verbose output
--release
Build artifacts in release mode
--no-default-features
Do not activate default features
-h, --help
Prints help information

Both switch and flag succeed if value is not present, switch returns true, flag returns second value.

$ app
Options { verbose: false, release: false, default_features: true }

When value is present - switch returns true, flag returns first value.

$ app --verbose --no-default-features --detailed
Error: --detailed is not expected in this context

Like with most parsrs unless specified switch and flag consume at most one item from the command line:

$ app --no-default-features --no-default-features
Error: argument --no-default-features cannot be used multiple times in this context
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/dynamic.rs (line 22)
19
20
21
22
23
24
fn bool(name: &'static str) -> impl Parser<(String, Value)> {
    let label = name.to_string();
    long(name)
        .switch()
        .map(move |n| (label.clone(), Value::Bool(n)))
}
examples/dd.rs (line 55)
51
52
53
54
55
56
57
58
59
60
61
62
63
pub fn options() -> OptionParser<Options> {
    let magic = short('m')
        .long("magic")
        .help("a usual switch still works")
        .switch();
    construct!(Options {
        magic,
        in_file(),
        out_file(),
        block_size(),
    })
    .to_options()
}
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/customize_help.rs (line 9)
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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")
        .with_usage(|doc| {
            let mut u = Doc::default();
            u.emphasis("You can call it with following flags:");
            u.text(" ");
            u.doc(&doc);
            u
        })
        .run();

    println!("{:?}", opt);
}
examples/xorg.rs (line 48)
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
pub fn options() -> OptionParser<Options> {
    let backing = toggle_options("(+|-)backing", "backing", "Set backing status").fallback(false);
    let xinerama =
        toggle_options("(+|-)xinerama", "xinerama", "Set 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()
}
source

pub fn flag<T>(self, present: T, absent: T) -> ParseFlag<T>
where T: Clone + 'static,

Flag with custom present/absent values

More generic version of switch that can use arbitrary type instead of bool.

Combinatoric example
#[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()
}

fn main() {
    println!("{:?}", options().run())
}
Derive example
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    /// Positive decision
    #[bpaf(flag(Decision::Yes, Decision::No))]
    decision: Decision,
}

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

fn main() {
    println!("{:?}", options().run())
}
Output

In --help output bpaf shows flags with no meta variable attached

$ app --help

Usage: app [--decision]

Available options:
--decision
Positive decision
-h, --help
Prints help information

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 36)
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
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(());

    // Try to apply the inner parser as many times as it succeeds, return the number
    let verbose = verbose.count();

    // 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);
}
source

pub fn req_flag<T>(self, present: T) -> impl Parser<T>
where T: Clone + 'static,

Required flag with custom value

Similar to flag takes no option arguments, but would only succeed if user specifies its name on a command line. Works best in combination with other parsers.

In derive style API bpaf would transform field-less enum variants into a parser that accepts one of it’s variant names as req_flag. Additionally bpaf handles () fields as req_flag.

Combinatoric example
#[derive(Debug, Clone)]
pub enum Style {
    Intel,
    Att,
    Llvm,
}

#[derive(Debug, Clone)]
pub enum Report {
    /// Include defailed report
    Detailed,
    /// Include minimal report
    Minimal,
    /// No preferences
    Undecided,
}

#[derive(Debug, Clone)]
pub struct Options {
    agree: (),
    style: Style,
    report: Report,
}

pub fn options() -> OptionParser<Options> {
    let agree = long("agree")
        .help("You must agree to perform the action")
        .req_flag(());

    let intel = long("intel")
        .help("Show assembly using Intel style")
        .req_flag(Style::Intel);
    let att = long("att")
        .help("Show assembly using AT&T style")
        .req_flag(Style::Att);
    let llvm = long("llvm").help("Show llvm-ir").req_flag(Style::Llvm);
    let style = construct!([intel, att, llvm]);

    let detailed = long("detailed")
        .help("Include detailed report")
        .req_flag(Report::Detailed);
    let minimal = long("minimal")
        .help("Include minimal report")
        .req_flag(Report::Minimal);
    let report = construct!([detailed, minimal]).fallback(Report::Undecided);

    construct!(Options {
        agree,
        style,
        report
    })
    .to_options()
}

fn main() {
    println!("{:?}", options().run())
}
Derive example
#[derive(Debug, Clone, Bpaf)]
pub enum Style {
    /// Show assembly using Intel style
    Intel,
    /// Show assembly using AT&T style
    Att,
    /// Show llvm-ir
    Llvm,
}

#[derive(Debug, Clone, Bpaf)]
#[bpaf(fallback(Report::Undecided))]
pub enum Report {
    /// Include detailed report
    Detailed,
    /// Include minimal report
    Minimal,
    #[bpaf(skip)]
    /// No preferences
    Undecided,
}

#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    /// You must agree to perform the action
    agree: (),
    // external here uses explicit reference to function `style`
    // generated above
    #[bpaf(external(style))]
    style: Style,
    // here reference is implicit and derived from field name: `report`
    #[bpaf(external)]
    report: Report,
}

fn main() {
    println!("{:?}", options().run())
}
Output

In --help message req_flag look similarly to switch and flag

$ app --help

Usage: app --agree (--intel | --att | --llvm) [--detailed | --minimal]

Available options:
--agree
You must agree to perform the action
--intel
Show assembly using Intel style
--att
Show assembly using AT&T style
--llvm
Show llvm-ir
--detailed
Include detailed report
--minimal
Include minimal report
-h, --help
Prints help information

Example contains two parsers that fails without any input: agree requires passing --agree

$ app
Error: expected --agree, pass --help for usage information

While style takes one of several possible values

$ app --agree
Error: expected --intel, --att, or more, pass --help for usage information

It is possible to alter the behavior using fallback or hide.

$ app --agree --intel
Options { agree: (), style: Intel, report: Undecided }

While parser for style takes any posted output - it won’t take multiple of them at once (unless other combinators such as many permit it) or last.

$ app --agree --att --llvm
Error: --llvm cannot be used at the same time as --att
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/env_logger.rs (line 31)
28
29
30
31
32
33
34
35
36
37
fn verbose() -> impl Parser<LevelFilter> {
    short('v')
        .help("Verbosity level, use multiple times for more verbosity")
        .req_flag(())
        .count()
        .map(|l| {
            use LevelFilter::*;
            [Off, Error, Warn, Info, Debug, Trace][l.max(5)]
        })
}
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/derive_show_asm.rs (line 98)
95
96
97
98
99
100
101
102
103
104
105
106
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::<_, Infallible>(|| {
        // we can call for supports-color crate here
        Ok(true)
    })
}
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 56)
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
fn opts() -> Opts {
    let sensor = long("sensor").req_flag(());
    let device = long("sensor-device")
        .argument::<String>("DEVICE")
        .complete(sensor_device_comp);
    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()
}
source

pub fn argument<T>(self, metavar: &'static str) -> ParseArgument<T>
where T: FromStr + 'static,

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 can specify the type with turbofish, for parsing types that don’t implement FromStr you can use consume a String/OsString first and parse it by hands.

For metavar value you should pick something short and descriptive about the parameter, usually in capital letters. For example for an abstract file parameter it could be "FILE", for a username - "USER", etc.

Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
    name: String,
    age: usize,
}

pub fn options() -> OptionParser<Options> {
    let name = short('n')
        .long("name")
        .help("Specify user name")
        // you can specify exact type argument should produce
        // for as long as it implements `FromStr`
        .argument::<String>("NAME");

    let age = long("age")
        .help("Specify user age")
        // but often rust can figure it out from the context,
        // here age is going to be `usize`
        .argument("AGE")
        .fallback(18)
        .display_fallback();

    construct!(Options { name, age }).to_options()
}

fn main() {
    println!("{:?}", options().run())
}
Derive example
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    // you can specify exact type argument should produce
    // for as long as it implements `FromStr`
    #[bpaf(short, long, argument::<String>("NAME"))]
    /// Specify user name
    name: String,
    // but often rust can figure it out from the context,
    // here age is going to be `usize`
    #[bpaf(argument("AGE"), fallback(18), display_fallback)]
    /// Specify user age
    age: usize,
}

fn main() {
    println!("{:?}", options().run())
}
Output
$ app --help

Usage: app -n=NAME [--age=AGE]

Available options:
-n, --name=NAME
Specify user name
--age=AGE
Specify user age
[default: 18]
-h, --help
Prints help information

--help shows arguments as a short name with attached metavariable

Value can be separated from flag by space, = sign

$ app --name Bob --age 12
Options { name: "Bob", age: 12 }
$ app --name "Bob" --age=12
Options { name: "Bob", age: 12 }
$ app --name=Bob
Options { name: "Bob", age: 18 }
$ app --name="Bob"
Options { name: "Bob", age: 18 }

Or in case of short name - be directly adjacent to it

$ app -nBob
Options { name: "Bob", age: 18 }

For long names - this doesn’t work since parser can’t tell where name stops and argument begins:

$ app --age12
Error: no such flag: --age12, did you mean --age?

Either way - value is required, passing just the argument name results in parse failure

$ app --name
Error: --name requires an argument NAME

You can further restrict it using adjacent

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/dynamic.rs (line 15)
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
fn number(name: &'static str) -> impl Parser<(String, Value)> {
    let label = name.to_string();
    long(name)
        .argument::<usize>("NUM")
        .map(move |n| (label.clone(), Value::Number(n)))
}

fn bool(name: &'static str) -> impl Parser<(String, Value)> {
    let label = name.to_string();
    long(name)
        .switch()
        .map(move |n| (label.clone(), Value::Bool(n)))
}

fn string(name: &'static str) -> impl Parser<(String, Value)> {
    let label = name.to_string();
    long(name)
        .help("this can use a help message")
        .argument::<String>("NUM")
        .map(move |n| (label.clone(), Value::String(n)))
}
examples/enum_in_args.rs (line 32)
28
29
30
31
32
33
34
35
36
37
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/many_comma_separated_args.rs (line 9)
6
7
8
9
10
11
12
13
14
15
16
17
fn args() -> impl Parser<Vec<u16>> {
    long("ports")
        .help("Comma separated list of ports")
        .argument::<String>("PORTS")
        .parse(|s| {
            s.split(',')
                .map(u16::from_str)
                .collect::<Result<Vec<_>, _>>()
        })
        .many()
        .map(|nested| nested.into_iter().flatten().collect())
}

Trait Implementations§

source§

impl Clone for NamedArg

source§

fn clone(&self) -> NamedArg

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for NamedArg

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

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

source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.