pub trait Parser<T> {
    fn many(self) -> ParseMany<Self>
    where
        Self: Sized
, { ... } fn some(self, message: &'static str) -> ParseSome<Self>
    where
        Self: Sized + Parser<T>
, { ... } fn optional(self) -> ParseOptional<Self>
    where
        Self: Sized + Parser<T>
, { ... } fn parse<F, R, E>(self, f: F) -> ParseWith<T, Self, F, E, R>
    where
        Self: Sized + Parser<T>,
        F: Fn(T) -> Result<R, E>,
        E: ToString
, { ... } fn map<F, R>(self, map: F) -> ParseMap<T, Self, F, R>
    where
        Self: Sized + Parser<T>,
        F: Fn(T) -> R + 'static
, { ... } fn from_str<R>(self) -> ParseFromStr<Self, R>
    where
        Self: Sized + Parser<T>
, { ... } fn guard<F>(self, check: F, message: &'static str) -> ParseGuard<Self, F>
    where
        Self: Sized + Parser<T>,
        F: Fn(&T) -> bool
, { ... } fn fallback(self, value: T) -> ParseFallback<Self, T>
    where
        Self: Sized + Parser<T>
, { ... } fn fallback_with<F, E>(self, fallback: F) -> ParseFallbackWith<T, Self, F, E>
    where
        Self: Sized + Parser<T>,
        F: Fn() -> Result<T, E>,
        E: ToString
, { ... } fn hide(self) -> ParseHide<Self>
    where
        Self: Sized + Parser<T>
, { ... } fn group_help(self, message: &'static str) -> ParseGroupHelp<Self>
    where
        Self: Sized + Parser<T>
, { ... } fn to_options(self) -> OptionParser<T>
    where
        Self: Sized + Parser<T> + 'static
, { ... } }
Expand description

Simple or composed argument parser

Overview

It’s best to think of an object implementing Parser trait as a container with a value inside that are composable with other Parser containers using construct! and the only way to extract this value is by transforming it to OptionParser with to_options and running it with run. At which point you either get your value out or bpaf would generate a message describing a problem (missing argument, validation failure, user requested help, etc) and the program would exit.

Values inside can be of any type for as long as they implement Debug, Clone and there’s no lifetimes other than static.

When consuming the values you usually start with Parser<String> or Parser<OsString> which you then transform into something that your program would actually use. it’s better to perform as much parsing and validation inside the Parser as possible so the program itself gets strictly typed and correct value while user gets immediate feedback on what’s wrong with the arguments they pass.

For example suppose your program needs user to specify a dimensions of a rectangle, with sides being 1..20 units long and the total area must not exceed 200 units square. A parser that consumes it might look like this:

#[derive(Debug, Copy, Clone)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn rectangle() -> impl Parser<Rectangle> {
    let invalid_size = "Sides of a rectangle must be 1..20 units long";
    let invalid_area = "Area of a rectangle must not exceed 200 units square";
    let width = long("width")
        .help("Width of the rectangle")
        .argument("PX")
        .from_str::<u32>()
        .guard(|&x| 1 <= x && x <= 10, invalid_size);
    let height = long("height")
        .help("Height of the rectangle")
        .argument("PX")
        .from_str::<u32>()
        .guard(|&x| 1 <= x && x <= 10, invalid_size);
    construct!(Rectangle { width, height })
        .guard(|&r| r.width * r.height <= 400, invalid_area)
}

Derive specific considerations

Every method defined on this trait belongs to the postprocessing section of the field annotation. bpaf_derive would try to figure out what chain to use for as long as there’s no options changing the type: you can use fallback, fallback_with, guard, hide and group_help but not the rest of them.

#[derive(Debug, Clone, Bpaf)]
struct Options {
    // no annotation at all - `bpaf_derive` inserts implicit `argument` and `from_str`
    number_1: u32,

    // fallback isn't changing the type so `bpaf_derive` still handles it
    #[bpaf(fallback(42))]
    number_2: u32,

    // `bpaf_derive` inserts implicit `argument`, `optional` and `from_str`
    number_3: Option<u32>,

    // fails to compile: you need to specify a consumer, `argument` or `argument_os`
    // #[bpaf(optional)]
    // number_4: Option<u32>

    // fails to compile: you also need to specify how to go from String to u32
    // #[bpaf(argument("N"), optional)]
    // number_5: Option<u32>,

    // explicit consumer and a full postprocessing chain
    #[bpaf(argument("N"), from_str(u32), optional)]
    number_6: Option<u32>,
}

Provided Methods

Consume zero or more items from a command line and collect them into Vec

many only collects elements that only consume something from the argument list.

Combinatoric usage:
fn numbers() -> impl Parser<Vec<u32>> {
    short('n')
        .argument("NUM")
        .from_str::<u32>()
        .many()
}
Derive usage:

bpaf would insert implicit many when resulting type is a vector

#[derive(Debug, Clone, Bpaf)]
struct Options {
    #[bpaf(short, argument("NUM"))]
    numbers: Vec<u32>
}

But it’s also possible to specify it explicitly, both cases renerate the same code. Note, since using many resets the postprocessing chain - you also need to specify from_str

#[derive(Debug, Clone, Bpaf)]
struct Options {
    #[bpaf(short, argument("NUM"), from_str(u32), many)]
    numbers: Vec<u32>
}
Example
$ app -n 1 -n 2 -n 3
// [1, 2, 3]
See also

some also collects results to a vector but requires at least one element to succeed

Examples found in repository?
examples/at_least_two.rs (line 10)
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 47)
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/positional.rs (line 18)
12
13
14
15
16
17
18
19
20
21
22
fn main() {
    let value = long("value")
        .help("Mysterious value")
        .argument("VAL")
        .from_str::<u32>()
        .fallback(42);
    let files = positional_os("FILE").map(PathBuf::from).many();
    let opts = construct!(Options { value, files }).to_options().run();

    println!("{:#?}", opts);
}
examples/top_to_bottom.rs (line 45)
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
78
79
80
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("SPEED")
        .from_str()
        .fallback(42.0)
}

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

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

fn files_to_process() -> impl Parser<Vec<PathBuf>> {
    short('f')
        .long("file")
        .help("File to process")
        .argument_os("FILE")
        .map(PathBuf::from)
        .many()
}
examples/env_logger.rs (line 32)
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 33)
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")
}

Consume one or more items from a command line

Takes a string used as an error message if there’s no specified parameters

some only collects elements that only consume something from the argument list.

Combinatoric usage:
let numbers
    = short('n')
    .argument("NUM")
    .from_str::<u32>()
    .some("Need at least one number");
Derive usage

Since using some resets the postprocessing chain - you also need to specify from_str or similar, depending on your type

#[derive(Debug, Clone, Bpaf)]
struct Options {
    #[bpaf(short, argument("NUM"), from_str(u32), some("Need at least one number"))]
    numbers: Vec<u32>
}
Example
$ app
// fails with "Need at least one number"
$ app -n 1 -n 2 -n 3
// [1, 2, 3]
See also

many also collects results to a vector but succeeds with no matching values

Turn a required argument into optional one

optional converts any failure caused by missing items into is None and passes the remaining parsing failures untouched.

Combinatoric usage
fn number() -> impl Parser<Option<u32>> {
    short('n')
        .argument("NUM")
        .from_str::<u32>()
        .optional()
}
Derive usage

By default bpaf_derive would automatically use optional for fields of type Option<T>, for as long as it’s not prevented from doing so by present postprocessing options

#[derive(Debug, Clone, Bpaf)]
struct Options {
   #[bpaf(short, argument("NUM"))]
   number: Option<u32>
}

But it’s also possible to specify it explicitly, in which case you need to specify a full postprocessing chain which starts from from_str in this example.

#[derive(Debug, Clone, Bpaf)]
struct Options {
   #[bpaf(short, argument("NUM"), from_str(u32), optional)]
   number: Option<u32>
}
Example
$ app
// None
$ app -n 42
// Some(42)
Examples found in repository?
examples/derive_commands.rs (line 36)
33
34
35
36
37
38
39
40
41
42
43
fn feature_if() -> impl Parser<Option<String>> {
    positional("FEATURE")
        .guard(move |s| !is_version(s), "")
        .optional()
}

fn version_if() -> impl Parser<Option<String>> {
    positional("VERSION")
        .guard(move |s| is_version(s), "")
        .optional()
}
More examples
Hide additional examples
examples/enum_tuple.rs (line 20)
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("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/cargo-cmd.rs (line 22)
16
17
18
19
20
21
22
23
24
25
26
27
28
fn main() {
    // defining a parser in a usual way
    let width = short('w').argument("WIDTH").from_str().fallback(10);
    let height = short('h').argument("HEIGHT").from_str().fallback(10);
    let parser = construct!(Opts { width, height });

    let cmd = positional("").guard(|s| s == "cmd", "").optional().hide();
    let combined_parser = construct!(cmd, parser).map(|x| x.1);

    let opts = combined_parser.to_options().run();

    println!("{:?}", opts);
}
examples/confusing.rs (line 34)
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("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("ARG").from_str::<i32>();
    let complex1 = command(
        "complex1",
        construct!(PreCommand::Complex1(complex1_parser))
            .to_options()
            .descr("This is complex command 1"),
    );

    let complex2_parser = positional("ARG").from_str::<i16>();
    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);
}

Apply a failing transformation to a contained value

This is a most general of transforming parsers and you can express remaining ones terms of it: map, from_str and guard.

Examples given here are a bit artificail, to parse a value from string you should use from_str.

Combinatoric usage:
fn number() -> impl Parser<u32> {
    short('n')
        .argument("NUM")
        .parse(|s| u32::from_str(&s))
}
Derive usage:

parse takes a single parameter: function name to call. Function type should match parameter F used by parse in combinatoric API.

fn read_number(s: String) -> Result<u32, ParseIntError> {
    u32::from_str(&s)
}

#[derive(Debug, Clone, Bpaf)]
struct Options {
    #[bpaf(short, argument("NUM"), parse(read_number))]
    number: u32
}
Example
$ app -n 12
// 12
// fails with "Couldn't parse "pi": invalid numeric literal"
Examples found in repository?
examples/derive_show_asm.rs (lines 56-64)
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
fn parse_manifest_path() -> impl Parser<PathBuf> {
    long("manifest-path")
        .help("Path to Cargo.toml")
        .argument_os("PATH")
        .map(PathBuf::from)
        .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")))
}
More examples
Hide additional examples
examples/confusing.rs (lines 61-71)
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("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("ARG").from_str::<i32>();
    let complex1 = command(
        "complex1",
        construct!(PreCommand::Complex1(complex1_parser))
            .to_options()
            .descr("This is complex command 1"),
    );

    let complex2_parser = positional("ARG").from_str::<i16>();
    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);
}

Apply a pure transformation to a contained value

A common case of parse method, exists mostly for convenience.

Combinatoric usage
fn number() -> impl Parser<u32> {
    short('n')
        .argument("NUM")
        .from_str::<u32>()
        .map(|v| v * 2)
}
Derive usage
fn double(num: u32) -> u32 {
    num * 2
}

#[derive(Debug, Clone, Bpaf)]
struct Options {
    #[bpaf(short, argument("NUM"), from_str(u32), map(double))]
    number: u32,
}
Example
$ app -n 21
// 42
Examples found in repository?
examples/derive_show_asm.rs (line 48)
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
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_os("PATH")
        .map(PathBuf::from)
        .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")))
}
More examples
Hide additional examples
examples/positional.rs (line 18)
12
13
14
15
16
17
18
19
20
21
22
fn main() {
    let value = long("value")
        .help("Mysterious value")
        .argument("VAL")
        .from_str::<u32>()
        .fallback(42);
    let files = positional_os("FILE").map(PathBuf::from).many();
    let opts = construct!(Options { value, files }).to_options().run();

    println!("{:#?}", opts);
}
examples/top_to_bottom.rs (line 46)
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
78
79
80
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("SPEED")
        .from_str()
        .fallback(42.0)
}

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

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

fn files_to_process() -> impl Parser<Vec<PathBuf>> {
    short('f')
        .long("file")
        .help("File to process")
        .argument_os("FILE")
        .map(PathBuf::from)
        .many()
}
examples/env_logger.rs (lines 33-38)
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 34)
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 28)
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("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);
}

Parse stored String using FromStr instance

A common case of parse method, exists mostly for convenience.

Combinatoric usage
fn speed() -> impl Parser<f64> {
    short('s')
        .argument("SPEED")
        .from_str::<f64>()
}
Derive usage

By default bpaf_derive would use from_str for any time it’s not familiar with so you don’t need to specify anything

#[derive(Debug, Clone, Bpaf)]
struct Options {
    #[bpaf(short, argument("SPEED"))]
    speed: f64
}

But it’s also possible to specify it explicitly

#[derive(Debug, Clone, Bpaf)]
struct Options {
    #[bpaf(short, argument("SPEED"), from_str(f64))]
    speed: f64
}
Example
$ app -s pi
// fails with "Couldn't parse "pi": invalid float literal"
$ app -s 3.1415
// Version: 3.1415
See also

Other parsing and restricting methods include parse and guard. For transformations that can’t fail you can use map.

Examples found in repository?
examples/top_to_bottom.rs (line 56)
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
fn speed() -> impl Parser<f64> {
    short('s')
        .long("speed")
        .help("Set speed")
        .argument("SPEED")
        .from_str()
        .fallback(42.0)
}

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

// no magical name transmogrifications.
fn nb_cars() -> impl Parser<u32> {
    short('n').long("nb-cars").argument("N").from_str()
}
More examples
Hide additional examples
examples/enum_in_args.rs (line 30)
25
26
27
28
29
30
31
32
33
34
35
fn main() {
    let opt = long("baz")
        .short('b')
        .help("choose between foo, bar or foobar")
        .argument("CMD")
        .from_str::<Baz>()
        .to_options()
        .run();

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

    println!("{:#?}", opts);
}
examples/cargo-cmd.rs (line 18)
16
17
18
19
20
21
22
23
24
25
26
27
28
fn main() {
    // defining a parser in a usual way
    let width = short('w').argument("WIDTH").from_str().fallback(10);
    let height = short('h').argument("HEIGHT").from_str().fallback(10);
    let parser = construct!(Opts { width, height });

    let cmd = positional("").guard(|s| s == "cmd", "").optional().hide();
    let combined_parser = construct!(cmd, parser).map(|x| x.1);

    let opts = combined_parser.to_options().run();

    println!("{:?}", opts);
}
examples/negative.rs (line 6)
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
    let age = long("age").argument("AGE").from_str::<i64>();
    let msg = "\
To pass a value that starts with a dash requres one one of two special syntaxes:

This will pass '-1' to '--age' handler and leave remaining arguments as is
    --age=-1
This will transform everything after '--' into non flags, '--age' will handle '-1'
and positional handlers will be able to handle the rest.
    --age -- -1";
    let num = age.to_options().descr(msg).run();
    println!("age: {}", num);
}
examples/simple.rs (line 16)
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
fn opts() -> Opts {
    let speed = short('k')
        .long("speed") // give it a name
        .help("speed in KPH") // and help message
        .argument("SPEED") // it's an argument with metavar
        .from_str::<f64>() // that is parsed from string as f64
        .map(|s| s / 0.62); // and converted to mph

    let distance = short('d')
        .long("distance")
        .help("distance in miles")
        .argument("DISTANCE")
        .from_str::<f64>();

    (construct!(Opts { speed, distance }))
        .to_options()
        .descr("Accept speed and distance, print them.")
        .run()
}

Validate or fail with a message

If value doesn’t satisfy the constraint - parser fails with the specified error message.

Combinatoric usage
fn number() -> impl Parser<u32> {
    short('n')
        .argument("NUM")
        .from_str::<u32>()
        .guard(|n| *n <= 10, "Values greater than 10 are only available in the DLC pack!")
}
Derive usage

Unlike combinator counterpart, derive variant of guard takes a function name instead of a closure, mostly to keep thing clean. Second argument can be either a string literal or a constant name for a static str.

fn dlc_check(number: &u32) -> bool {
    *number <= 10
}

const DLC_NEEDED: &str = "Values greater than 10 are only available in the DLC pack!";

#[derive(Debug, Clone, Bpaf)]
struct Options {
    #[bpaf(short, argument("NUM"), guard(dlc_check, DLC_NEEDED))]
    number: u32,
}
Example
$ app -n 100
// fails with "Values greater than 10 are only available in the DLC pack!"
$ app -n 5
// 5
Examples found in repository?
examples/derive_commands.rs (line 35)
33
34
35
36
37
38
39
40
41
42
43
fn feature_if() -> impl Parser<Option<String>> {
    positional("FEATURE")
        .guard(move |s| !is_version(s), "")
        .optional()
}

fn version_if() -> impl Parser<Option<String>> {
    positional("VERSION")
        .guard(move |s| is_version(s), "")
        .optional()
}
More examples
Hide additional examples
examples/at_least_two.rs (line 11)
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);
}
examples/top_to_bottom.rs (line 47)
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.rs (line 35)
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/cargo-cmd.rs (line 22)
16
17
18
19
20
21
22
23
24
25
26
27
28
fn main() {
    // defining a parser in a usual way
    let width = short('w').argument("WIDTH").from_str().fallback(10);
    let height = short('h').argument("HEIGHT").from_str().fallback(10);
    let parser = construct!(Opts { width, height });

    let cmd = positional("").guard(|s| s == "cmd", "").optional().hide();
    let combined_parser = construct!(cmd, parser).map(|x| x.1);

    let opts = combined_parser.to_options().run();

    println!("{:?}", opts);
}
examples/basic.rs (line 32)
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
fn main() {
    // A flag, true if used in the command line. Can be required, this one is optional
    let debug = short('d')
        .long("debug")
        .help("Activate debug mode")
        .switch();

    // number of occurrences of the v/verbose flag capped at 3 with an error here but you can also
    // use `max` inside `map`
    let verbose = 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
    let speed = short('s')
        .long("speed")
        .help("Set speed")
        .argument("SPEED")
        .from_str()
        .fallback(42.0);

    let output = short('o')
        .long("output")
        .help("output file")
        .argument_os("OUTPUT")
        .map(PathBuf::from);

    // no magical name transmogrifications in combinatoric API
    let nb_cars = short('n').long("nb-cars").argument("N").from_str();

    // a parser that consumes one argument
    let file_to_proces = short('f')
        .long("file")
        .help("File to process")
        .argument_os("FILE")
        .map(PathBuf::from);
    let files_to_process = file_to_proces.many();

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

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

Use this value as default if value isn’t present on a command line

Parser would still fail if value is present but failure comes from some transformation

Combinatoric usage
fn number() -> impl Parser<u32> {
    short('n')
        .argument("NUM")
        .from_str::<u32>()
        .fallback(42)
}
Derive usage

Expression in parens should have the right type, this example uses u32 literal, but it can also be your own type if that’s what you are parsing, it can also be a function call.

#[derive(Debug, Clone, Bpaf)]
struct Options {
   #[bpaf(short, argument("NUM"), from_str(u32), fallback(42))]
   number: u32
}
Example
$ app -n 100
// 10
$ app
// 42
$ app -n pi
// fails with "Couldn't parse "pi": invalid numeric literal"
See also

fallback_with would allow to try to fallback to a value that comes from a failing computation such as reading a file.

Examples found in repository?
examples/top_to_bottom.rs (line 57)
51
52
53
54
55
56
57
58
fn speed() -> impl Parser<f64> {
    short('s')
        .long("speed")
        .help("Set speed")
        .argument("SPEED")
        .from_str()
        .fallback(42.0)
}
More examples
Hide additional examples
examples/positional.rs (line 17)
12
13
14
15
16
17
18
19
20
21
22
fn main() {
    let value = long("value")
        .help("Mysterious value")
        .argument("VAL")
        .from_str::<u32>()
        .fallback(42);
    let files = positional_os("FILE").map(PathBuf::from).many();
    let opts = construct!(Options { value, files }).to_options().run();

    println!("{:#?}", opts);
}
examples/cargo-cmd.rs (line 18)
16
17
18
19
20
21
22
23
24
25
26
27
28
fn main() {
    // defining a parser in a usual way
    let width = short('w').argument("WIDTH").from_str().fallback(10);
    let height = short('h').argument("HEIGHT").from_str().fallback(10);
    let parser = construct!(Opts { width, height });

    let cmd = positional("").guard(|s| s == "cmd", "").optional().hide();
    let combined_parser = construct!(cmd, parser).map(|x| x.1);

    let opts = combined_parser.to_options().run();

    println!("{:?}", opts);
}
examples/multiple_fallback.rs (line 31)
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
pub fn main() {
    let field1 = long("field1")
        .env("FIELD1")
        .help("Field 1")
        .argument("ARG")
        .from_str::<u32>()
        .fallback(DEFAULT_CONFIG.field1);
    let field2 = long("field2")
        .env("FIELD2")
        .help("Field 2")
        .argument("ARG")
        .from_str::<u64>()
        .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);
}
examples/no_import.rs (lines 18-26)
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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::Parser::from_str::<f64>(
            bpaf::short('s')
                .long("speed")
                .help("Set speed")
                .argument("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);
}
examples/git.rs (line 27)
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
54
55
56
57
fn main() {
    let dry_run = long("dry_run").switch();
    let all = long("all").switch();
    let repository = positional("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("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);
}

Use value produced by this function as default if value isn’t present

Would still fail if value is present but failure comes from some earlier transformation

Combinatoric usage
fn username() -> impl Parser<String> {
    long("user")
        .argument("USER")
        .fallback_with::<_, Box<dyn std::error::Error>>(||{
            let output = std::process::Command::new("whoami")
                .stdout(std::process::Stdio::piped())
                .spawn()?
                .wait_with_output()?
                .stdout;
            Ok(std::str::from_utf8(&output)?.to_owned())
        })
}
Derive usage
fn get_current_user() -> Result<String, Box<dyn std::error::Error>> {
    let output = std::process::Command::new("whoami")
        .stdout(std::process::Stdio::piped())
        .spawn()?
        .wait_with_output()?
        .stdout;
    Ok(std::str::from_utf8(&output)?.to_owned())
}

#[derive(Debug, Clone, Bpaf)]
struct Options {
    #[bpaf(long, argument("USER"), fallback_with(get_current_user))]
    user: String,
}
Example
$ app --user bobert
// "bobert"
$ app
// "pacak"
See also

fallback implements similar logic expect that failures aren’t expected.

Examples found in repository?
examples/derive_show_asm.rs (line 65)
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
105
fn parse_manifest_path() -> impl Parser<PathBuf> {
    long("manifest-path")
        .help("Path to Cargo.toml")
        .argument_os("PATH")
        .map(PathBuf::from)
        .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))
}

Ignore this parser during any sort of help generation

Best used for optional parsers or parsers with a defined fallback, usually for implementing backward compatibility or hidden aliases

Combinatoric usage
/// bpaf would accept both `-W` and `-H` flags, but the help message
/// would contain only `-H`
fn rectangle() -> impl Parser<(u32, u32)> {
    let width = short('W')
        .argument("PX")
        .from_str::<u32>()
        .fallback(10)
        .hide();
    let height = short('H')
        .argument("PX")
        .from_str::<u32>()
        .fallback(10)
        .hide();
    construct!(width, height)
}
Example
$ app -W 12 -H 15
// (12, 15)
$ app -H 333
// (10, 333)
$ app --help
// contains -H but not -W
Derive usage
#[derive(Debug, Clone, Bpaf)]
struct Rectangle {
    #[bpaf(short('W'), argument("PX"), from_str(u32), fallback(10), hide)]
    width: u32,
    #[bpaf(short('H'), argument("PX"), from_str(u32))]
    height: u32,
}
Example
$ app -W 12 -H 15
// Rectangle { width: 12, height: 15 }
$ app -H 333
// Rectangle { width: 10, height: 333 }
$ app --help
// contains -H but not -W
Examples found in repository?
examples/cargo-cmd.rs (line 22)
16
17
18
19
20
21
22
23
24
25
26
27
28
fn main() {
    // defining a parser in a usual way
    let width = short('w').argument("WIDTH").from_str().fallback(10);
    let height = short('h').argument("HEIGHT").from_str().fallback(10);
    let parser = construct!(Opts { width, height });

    let cmd = positional("").guard(|s| s == "cmd", "").optional().hide();
    let combined_parser = construct!(cmd, parser).map(|x| x.1);

    let opts = combined_parser.to_options().run();

    println!("{:?}", opts);
}

Attach help message to a complex parser

bpaf inserts the group help message before the block with all the fields from the inner parser and an empty line after the block.

Combinatoric usage
fn rectangle() -> impl Parser<(u32, u32)> {
    let width = short('w')
        .argument("PX")
        .from_str::<u32>();
    let height = short('h')
        .argument("PX")
        .from_str::<u32>();
    construct!(width, height)
        .group_help("Takes a rectangle")
}
Example
$ app --help
<skip>
            Takes a rectangle
   -w <PX>  Width of the rectangle
   -h <PX>  Height of the rectangle

<skip>
Derive usage
#[derive(Debug, Clone, Bpaf)]
struct Rectangle {
    width: u32,
    height: u32,
}

#[derive(Debug, Clone, Bpaf)]
struct Options {
    #[bpaf(external, group_help("Takes a rectangle"))]
    rectangle: Rectangle
}
Examples found in repository?
examples/rectangle.rs (line 33)
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
fn main() {
    let width = short('w')
        .long("width")
        .help("Width of the rectangle")
        .argument("PX")
        .from_str::<usize>();

    let height = short('h')
        .long("height")
        .help("Height of the rectangle")
        .argument("PX")
        .from_str::<usize>();

    let rect = construct!(Rect { width, height })
        .group_help("Rectangle is defined by width and height in meters");

    let verbose = short('v')
        .long("verbose")
        .help("Print computation steps")
        .switch();

    let opt = construct!(Out { verbose, rect })
        .to_options()
        .descr("This program calculates rectangle's area")
        .header("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
        .footer("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^")
        .run();
    println!("{:#?}", opt);
}

Transform Parser into OptionParser to attach metadata and run

Combinatoric usage
fn parser() -> impl Parser<u32> {
    short('i')
        .argument("ARG")
        .from_str::<u32>()
}

fn option_parser() -> OptionParser<u32> {
    parser()
        .to_options()
        .version("3.1415")
        .descr("This is a description")
}

See OptionParser for more methods available after conversion.

Derive usage

Add a top level options annotation to generate OptionParser instead of default Parser.

In addition to options annotation you can also specify either version or version(value) annotation. Former uses version from cargo, later uses the specified value which should be an expression of type &'static str, see version.

#[derive(Debug, Clone, Bpaf)]
#[bpaf(options, version("3.1415"))]
/// This is a description
struct Options {
   verbose: bool,
}
Example
$ app --version
// Version: 3.1415
$ app --help
<skip>
This is a description
<skip>
Examples found in repository?
examples/at_least_two.rs (line 12)
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_variable.rs (line 17)
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("KEY");

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

    println!("{:?}", opts);
}
examples/enum_in_args.rs (line 31)
25
26
27
28
29
30
31
32
33
34
35
fn main() {
    let opt = long("baz")
        .short('b')
        .help("choose between foo, bar or foobar")
        .argument("CMD")
        .from_str::<Baz>()
        .to_options()
        .run();

    println!("{:#?}", opt);
}
examples/top_to_bottom.rs (line 28)
18
19
20
21
22
23
24
25
26
27
28
29
30
31
fn main() {
    // packing things in a struct assumes parser for each field is in scope.
    let opt = (construct!(Out {
        debug(),
        verbose(),
        speed(),
        output(),
        nb_cars(),
        files_to_process()
    }))
    .to_options()
    .run();
    println!("{:#?}", opt);
}
examples/positional.rs (line 19)
12
13
14
15
16
17
18
19
20
21
22
fn main() {
    let value = long("value")
        .help("Mysterious value")
        .argument("VAL")
        .from_str::<u32>()
        .fallback(42);
    let files = positional_os("FILE").map(PathBuf::from).many();
    let opts = construct!(Options { value, files }).to_options().run();

    println!("{:#?}", opts);
}
examples/after_help.rs (line 10)
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()
        // help metadata
        .descr("I am a program and I do things")
        .header("Sometimes they even work.")
        .footer("Beware `-d`, dragons be here")
        .run();

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

Implementors