Trait bpaf::Parser

source ·
pub trait Parser<T> {
Show 17 methods 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 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 hide_usage(self) -> ParseHideUsage<Self>
    where
        Self: Sized + Parser<T>
, { ... } fn group_help(self, message: &'static str) -> ParseGroupHelp<Self>
    where
        Self: Sized + Parser<T>
, { ... } fn complete<M, F>(self, op: F) -> ParseComp<Self, F>
    where
        M: Into<String>,
        F: Fn(&T) -> Vec<(M, Option<M>)>,
        Self: Sized + Parser<T>
, { ... } fn complete_shell(self, op: ShellComp) -> ParseCompShell<Self>
    where
        Self: Sized + Parser<T>
, { ... } fn complete_style(self, style: CompleteDecor) -> ParseCompStyle<Self>
    where
        Self: Sized + Parser<T>
, { ... } fn adjacent(self) -> ParseAdjacent<Self>
    where
        Self: Sized + Parser<T>
, { ... } fn anywhere(self) -> ParseAnywhere<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 can jump straight to a value that implements FromStr trait then transform into something that your program would actually use. Alternatively you can consume either String or OsString and parse that by hand. 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::<u32>("PX")
        .guard(|&x| 1 <= x && x <= 10, invalid_size);
    let height = long("height")
        .help("Height of the rectangle")
        .argument::<u32>("PX")
        .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 gets the right type
    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 the right type
    number_3: Option<u32>,

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

    #[bpaf(argument("N"), optional)]
    number_5: Option<u32>,

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

Provided Methods§

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

many preserves any parsing falures and propagates them outwards, with extra catch statement you can instead stop at the first value that failed to parse and ignore it and all the subsequent ones.

many only collects elements that only consume something from the argument list. For derive usage bpaf_derive would insert implicit many when resulting type is a vector.

Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
    argument: Vec<u32>,
    switches: Vec<bool>,
}

pub fn options() -> OptionParser<Options> {
    let argument = long("argument")
        .help("important argument")
        .argument("ARG")
        .many();
    let switches = long("switch").help("some switch").switch().many();
    construct!(Options { argument, switches }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    /// important argument
    argument: Vec<u32>,
    /// some switch
    #[bpaf(long("switch"), switch)]
    switches: Vec<bool>,
}
Examples

Run inner parser as many times as possible collecting all the new results

% app --argument 10 --argument 20
Options { argument: [10, 20], switches: [] }

If there’s no matching parsers - it would produce an empty vector

% app 
Options { argument: [], switches: [] }

For parsers that can succeed without consuming anything such as flag or switch - many only collects values as long as they produce something

% app --switch --switch
Options { argument: [], switches: [true, true] }

In usage lines many items are indicated with ...

% app --help
Usage: --argument ARG... [--switch]...

Available options:
        --argument <ARG>  important argument
        --switch          some switch
    -h, --help            Prints help information
See also

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

Examples found in repository?
examples/shared_args.rs (line 25)
24
25
26
fn shared() -> impl Parser<Vec<String>> {
    positional("ARG").many()
}
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/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);
}
examples/ex_positional.rs (line 17)
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/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
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()
}
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)
        })
}

Consume one or more items from a command line

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

some preserves any parsing falures and propagates them outwards, with extra catch statement you can instead stop at the first value that failed to parse and ignore it and all the subsequent ones.

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

Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
    argument: Vec<u32>,
    switches: Vec<bool>,
}

pub fn options() -> OptionParser<Options> {
    let argument = long("argument")
        .help("important argument")
        .argument("ARG")
        .some("want at least one argument");
    let switches = long("switch")
        .help("some switch")
        .switch()
        .some("want at least one switch");
    construct!(Options { argument, switches }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    /// important argument
    #[bpaf(argument("ARG"), some("want at least one argument"))]
    argument: Vec<u32>,
    /// some switch
    #[bpaf(long("switch"), switch, some("want at least one switch"))]
    switches: Vec<bool>,
}
Examples

Run inner parser as many times as possible collecting all the new results, but unlike many needs to collect at least one element to succeed

% app --argument 10 --argument 20 --switch
Options { argument: [10, 20], switches: [true] }

With not enough parameters to satisfy both parsers at least once - it fails

% app 
want at least one argument

both parsers need to succeed to create a struct

% app --argument 10
want at least one switch

For parsers that can succeed without consuming anything such as flag or switch - many only collects values as long as they produce something

% app --switch --argument 10
Options { argument: [10], switches: [true] }

In usage lines some items are indicated with ...

% app --help
Usage: --argument ARG... [--switch]...

Available options:
        --argument <ARG>  important argument
        --switch          some switch
    -h, --help            Prints help information
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 missing items into is None and passes the remaining parsing failures untouched. With extra catch statement you can handle those failures too.

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. But it’s also possible to specify it explicitly.

Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
    version: Option<usize>,
    feature: Option<String>,
}
pub fn options() -> OptionParser<Options> {
    let version = long("version").argument("VERS").optional();
    let feature = long("feature").argument("FEAT").optional();
    construct!(Options { version, feature }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    #[bpaf(argument("VERS"))]
    version: Option<usize>,
    #[bpaf(argument("FEAT"))]
    feature: Option<String>,
}
Examples

Missing arguments are turned into None

% app 
Options { version: None, feature: None }

Present values are Some optional

% app --version 10
Options { version: Some(10), feature: None }

You can specify both

% app --version 10 --feature feat
Options { version: Some(10), feature: Some("feat") }

bpaf encases optional arguments in usage with []

% app --help
Usage: [--version VERS] [--feature FEAT]

Available options:
        --version <VERS>
        --feature <FEAT>
    -h, --help            Prints help information
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::<String>("FEATURE")
        .guard(move |s| !is_version(s), "")
        .optional()
}

fn version_if() -> impl Parser<Option<String>> {
    positional::<String>("VERSION")
        .guard(move |s| is_version(s), "")
        .optional()
}
More examples
Hide additional examples
src/batteries.rs (line 163)
157
158
159
160
161
162
163
164
165
166
167
pub fn cargo_helper<P, T>(cmd: &'static str, parser: P) -> impl Parser<T>
where
    P: Parser<T>,
{
    let skip = positional::<String>("cmd")
        .guard(move |s| s == cmd, "")
        .optional()
        .catch()
        .hide();
    construct!(skip, parser).map(|x| x.1)
}
src/lib.rs (line 1663)
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
pub fn cargo_helper<P, T>(cmd: &'static str, parser: P) -> impl Parser<T>
where
    T: 'static,
    P: Parser<T>,
{
    let skip = positional::<String>("cmd")
        .guard(move |s| s == cmd, "")
        .optional()
        .catch()
        .hide();
    construct!(skip, parser).map(|x| x.1)
}
examples/find.rs (line 38)
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
fn user() -> impl Parser<Option<String>> {
    let tag = any::<String>("TAG")
        .guard(|s| s == "-user", "not user")
        .hide();
    let value = positional::<String>("USER");
    construct!(tag, value)
        .anywhere()
        .map(|pair| pair.1)
        .optional()
        .catch()
}

// parsers -exec xxx yyy zzz ;
fn exec() -> impl Parser<Option<Vec<OsString>>> {
    let tag = any::<String>("-exec")
        .help("-exec /path/to/command flags and options ;")
        .guard(|s| s == "-exec", "not find");
    let item = any::<OsString>("ITEM")
        .guard(|s| s != ";", "not word")
        .many()
        .catch()
        .hide();
    let endtag = any::<String>("END").guard(|s| s == ";", "not eot").hide();
    construct!(tag, item, endtag)
        .anywhere()
        .map(|triple| triple.1)
        .optional()
        .catch()
}

/// parses symbolic permissions `-perm -mode`, `-perm /mode` and `-perm mode`
fn perm() -> impl Parser<Option<Perm>> {
    fn parse_mode(input: &str) -> Result<Perms, String> {
        let mut perms = Perms::default();
        for c in input.chars() {
            match c {
                'r' => perms.read = true,
                'w' => perms.write = true,
                'x' => perms.exec = true,
                _ => return Err(format!("{} is not a valid permission string", input)),
            }
        }
        Ok(perms)
    }

    let tag = any::<String>("-mode").help("-mode (perm | -perm | /perm)");
    let mode = any::<String>("mode")
        .parse::<_, _, String>(|s| {
            if let Some(m) = s.strip_prefix('-') {
                Ok(Perm::All(parse_mode(m)?))
            } else if let Some(m) = s.strip_prefix('/') {
                Ok(Perm::Any(parse_mode(m)?))
            } else {
                Ok(Perm::Exact(parse_mode(&s)?))
            }
        })
        .hide();

    construct!(tag, mode)
        .anywhere()
        .map(|pair| pair.1)
        .optional()
        .catch()
}
examples/numeric_prefix.rs (line 13)
10
11
12
13
14
15
16
17
18
pub fn options() -> OptionParser<Options> {
    let prefix = positional::<usize>("PREFIX")
        .help("Optional numeric command prefix")
        .optional()
        .catch();
    let command = positional::<String>("COMMAND").help("Required command name");

    construct!(Options { prefix, command }).to_options()
}
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::<String>("BAR")
        .optional();

    let bar_cmd = construct!(Foo { bar })
        .to_options()
        .descr("This command will try to do foo given a bar argument");

    let opt = command("foo", bar_cmd)
        .help("command for doing foo")
        .map(Command::Foo)
        .to_options()
        .run();

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

Apply a failing transformation to a contained value

Transformation preserves present/absent state of the value: to parse an optional value you can either first try to parse it and then mark as optional or first deal with the optionality and then parse a value wrapped in Option. In most cases former approach is more concise.

Similarly it is possible to parse multiple items with many or some by either parsing a single item first and then turning it into a Vec or collecting them into a Vec first and then parsing the whole vector. Former approach is more concise.

This is a most general of transforming parsers and you can express map and guard in terms of it.

Examples are a bit artificail, to parse a value from string you can specify the type directly in argument’s turbofish and then apply map.

Derive usage:

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

Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
    number: u32,
}
pub fn options() -> OptionParser<Options> {
    let number = long("number")
        .argument::<String>("N")
        // normally you'd use argument::<u32> and `map`
        .parse::<_, _, ParseIntError>(|s| Ok(u32::from_str(&s)? * 2));
    construct!(Options { number }).to_options()
}
Derive usage
fn twice_the_num(s: String) -> Result<u32, ParseIntError> {
    Ok(u32::from_str(&s)? * 2)
}

#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    #[bpaf(argument::<String>("N"), parse(twice_the_num))]
    number: u32,
}
Examples

You can use parse to apply arbitrary failing transformation to any input. normally --number takes a numerical value and doubles it

% app --number 10
Options { number: 20 }

But if function inside the parser fails - user will get the error back unless it’s handled in some way

% app --number ten
Couldn't parse "ten": invalid digit found in string
Examples found in repository?
examples/derive_show_asm.rs (lines 55-63)
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
fn parse_manifest_path() -> impl Parser<PathBuf> {
    long("manifest-path")
        .help("Path to Cargo.toml")
        .argument::<PathBuf>("PATH")
        .parse(|p| {
            if p.is_absolute() {
                Ok(p)
            } else {
                std::env::current_dir()
                    .map(|d| d.join(p))
                    .and_then(|full_path| full_path.canonicalize())
            }
        })
        .fallback_with(|| std::env::current_dir().map(|x| x.join("Cargo.toml")))
}
More examples
Hide additional examples
examples/dd.rs (lines 24-27)
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
fn tag<T>(name: &'static str, meta: &'static str, help: &'static str) -> impl Parser<T>
where
    T: FromStr,
    <T as std::str::FromStr>::Err: std::fmt::Display,
{
    // it is possible to parse OsString here and strip the prefix with os_str_bytes or a similar
    // crate
    any::<String>(meta)
        .help(help)
        .parse::<_, _, String>(move |s| match s.strip_prefix(name) {
            None => Err("Wrong tag".to_string()),
            Some(body) => T::from_str(body).map_err(|e| e.to_string()),
        })
        .anywhere()
}
examples/xorg.rs (lines 14-27)
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
fn toggle_options(name: &'static str, help: &'static str) -> impl Parser<bool> {
    any::<String>(name)
        .help(help)
        .parse(move |s| {
            let (state, cur_name) = if let Some(rest) = s.strip_prefix('+') {
                (true, rest)
            } else if let Some(rest) = s.strip_prefix('-') {
                (false, rest)
            } else {
                return Err(format!("{} is not a toggle option", s));
            };
            if cur_name != name {
                Err(format!("{} is not a known toggle option name", cur_name))
            } else {
                Ok(state)
            }
        })
        .anywhere()
}

fn extension() -> impl Parser<(String, bool)> {
    let on = any::<String>("+ext")
        .help("enable ext <EXT>")
        .parse::<_, _, String>(|s| {
            if s == "+ext" {
                Ok(true)
            } else {
                Err(String::new())
            }
        });
    let off = any::<String>("-ext")
        .help("disable ext <EXT>")
        .parse::<_, _, String>(|s| {
            if s == "-ext" {
                Ok(false)
            } else {
                Err(String::new())
            }
        });

    let state = construct!([on, off]);
    let name = positional::<String>("EXT").hide();
    construct!(state, name).map(|(a, b)| (b, a)).anywhere()
}
examples/cat.rs (lines 19-29)
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
fn main() {
    let file = positional::<OsString>("FILE")
        .help("File name to concatenate, with no FILE or when FILE is -, read standard input")
        .optional()
        .parse::<_, Box<dyn Read>, std::io::Error>(|path| {
            Ok(if let Some(path) = path {
                if path == "-" {
                    Box::new(stdin())
                } else {
                    Box::new(File::open(path)?)
                }
            } else {
                Box::new(stdin())
            })
        })
        .to_options()
        .descr("Concatenate a file to standard output")
        .run();

    let reader = BufReader::new(file);

    for line in reader.lines() {
        println!("{}", line.unwrap());
    }
}
examples/find.rs (lines 77-85)
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
fn perm() -> impl Parser<Option<Perm>> {
    fn parse_mode(input: &str) -> Result<Perms, String> {
        let mut perms = Perms::default();
        for c in input.chars() {
            match c {
                'r' => perms.read = true,
                'w' => perms.write = true,
                'x' => perms.exec = true,
                _ => return Err(format!("{} is not a valid permission string", input)),
            }
        }
        Ok(perms)
    }

    let tag = any::<String>("-mode").help("-mode (perm | -perm | /perm)");
    let mode = any::<String>("mode")
        .parse::<_, _, String>(|s| {
            if let Some(m) = s.strip_prefix('-') {
                Ok(Perm::All(parse_mode(m)?))
            } else if let Some(m) = s.strip_prefix('/') {
                Ok(Perm::Any(parse_mode(m)?))
            } else {
                Ok(Perm::Exact(parse_mode(&s)?))
            }
        })
        .hide();

    construct!(tag, mode)
        .anywhere()
        .map(|pair| pair.1)
        .optional()
        .catch()
}
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::<String>("TOKEN")
        .optional();

    // start with defining 3 commands: simple, complex1 and complex2
    let simple_parser = pure(PreCommand::Simple).to_options();
    let simple = command("simple", simple_parser);

    let complex1_parser = positional::<i32>("ARG");
    let complex1 = command(
        "complex1",
        construct!(PreCommand::Complex1(complex1_parser))
            .to_options()
            .descr("This is complex command 1"),
    );

    let complex2_parser = positional::<i16>("ARG");
    let complex2 = command(
        "complex1",
        construct!(PreCommand::Complex2(complex2_parser))
            .to_options()
            .descr("This is complex command 2"),
    );

    // compose then to accept any of those
    let preparser = construct!([simple, complex1, complex2]);

    // make a parser that accepts optional token and one of incomplete commands
    // then create complete command or fail
    let parser = construct!(token, preparser).parse(|(token, cmd)| match cmd {
        PreCommand::Simple => Ok(Command::Simple),
        PreCommand::Complex1(a) => match token {
            Some(token) => Ok(Command::Complex1(token, a)),
            None => Err("You must specify token to use with --token"),
        },
        PreCommand::Complex2(a) => match token {
            Some(token) => Ok(Command::Complex2(token, a)),
            None => Err("You must specify token to use with --token"),
        },
    });

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

Apply a pure transformation to a contained value

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

Derive usage:

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

Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
    number: u32,
}
pub fn options() -> OptionParser<Options> {
    let number = long("number").argument::<u32>("N").map(|x| x * 2);
    construct!(Options { number }).to_options()
}
Derive usage
fn twice_the_num(n: u32) -> u32 {
    n * 2
}

#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    #[bpaf(argument::<u32>("N"), map(twice_the_num))]
    number: u32,
}
Examples

You can use map to apply arbitrary pure transformation to any input. normally --number takes a numerical value and doubles it

% app --number 10
Options { number: 20 }

But if function inside the parser fails - user will get the error back unless it’s handled in some way

% app --number ten
Couldn't parse "ten": invalid digit found in string
Examples found in repository?
examples/derive_show_asm.rs (line 48)
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())
}
More examples
Hide additional examples
src/lib.rs (line 1666)
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
pub fn cargo_helper<P, T>(cmd: &'static str, parser: P) -> impl Parser<T>
where
    T: 'static,
    P: Parser<T>,
{
    let skip = positional::<String>("cmd")
        .guard(move |s| s == cmd, "")
        .optional()
        .catch()
        .hide();
    construct!(skip, parser).map(|x| x.1)
}
examples/find.rs (line 37)
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
fn user() -> impl Parser<Option<String>> {
    let tag = any::<String>("TAG")
        .guard(|s| s == "-user", "not user")
        .hide();
    let value = positional::<String>("USER");
    construct!(tag, value)
        .anywhere()
        .map(|pair| pair.1)
        .optional()
        .catch()
}

// parsers -exec xxx yyy zzz ;
fn exec() -> impl Parser<Option<Vec<OsString>>> {
    let tag = any::<String>("-exec")
        .help("-exec /path/to/command flags and options ;")
        .guard(|s| s == "-exec", "not find");
    let item = any::<OsString>("ITEM")
        .guard(|s| s != ";", "not word")
        .many()
        .catch()
        .hide();
    let endtag = any::<String>("END").guard(|s| s == ";", "not eot").hide();
    construct!(tag, item, endtag)
        .anywhere()
        .map(|triple| triple.1)
        .optional()
        .catch()
}

/// parses symbolic permissions `-perm -mode`, `-perm /mode` and `-perm mode`
fn perm() -> impl Parser<Option<Perm>> {
    fn parse_mode(input: &str) -> Result<Perms, String> {
        let mut perms = Perms::default();
        for c in input.chars() {
            match c {
                'r' => perms.read = true,
                'w' => perms.write = true,
                'x' => perms.exec = true,
                _ => return Err(format!("{} is not a valid permission string", input)),
            }
        }
        Ok(perms)
    }

    let tag = any::<String>("-mode").help("-mode (perm | -perm | /perm)");
    let mode = any::<String>("mode")
        .parse::<_, _, String>(|s| {
            if let Some(m) = s.strip_prefix('-') {
                Ok(Perm::All(parse_mode(m)?))
            } else if let Some(m) = s.strip_prefix('/') {
                Ok(Perm::Any(parse_mode(m)?))
            } else {
                Ok(Perm::Exact(parse_mode(&s)?))
            }
        })
        .hide();

    construct!(tag, mode)
        .anywhere()
        .map(|pair| pair.1)
        .optional()
        .catch()
}
examples/top_to_bottom.rs (line 46)
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/shared_args.rs (line 29)
28
29
30
31
32
33
34
fn parse_command() -> impl Parser<(Command, Vec<String>)> {
    let action = action().map(Command::Action);
    let action = construct!(action, shared()).to_options().command("action");
    let build = build().map(Command::Build);
    let build = construct!(build, shared()).to_options().command("build");
    construct!([action, build])
}
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)
        })
}

Validate or fail with a message

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

Derive usage

Derive variant of guard takes a function name instead of a closure, mostly to keep things clean. Second argument can be either a string literal or a constant name for a static str.

Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
    number: u32,
}
pub fn options() -> OptionParser<Options> {
    let number = long("number").argument::<u32>("N").guard(
        |n| *n <= 10,
        "Values greater than 10 are only available in the DLC pack!",
    );
    construct!(Options { number }).to_options()
}
Derive usage
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)]
#[bpaf(options)]
pub struct Options {
    #[bpaf(argument("N"), guard(dlc_check, DLC_NEEDED))]
    number: u32,
}
Examples

You can use guard to set boundary limits or perform other checks on parsed values. Numbers below 10: parser accepts number below 10

% app --number 5
Options { number: 5 }

But fails with the error message on higher values:

% app --number 11
"11": Values greater than 10 are only available in the DLC pack!

But if function inside the parser fails - user will get the error back unless it’s handled in some way

% app --number ten
Couldn't parse "ten": invalid digit found in string
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::<String>("FEATURE")
        .guard(move |s| !is_version(s), "")
        .optional()
}

fn version_if() -> impl Parser<Option<String>> {
    positional::<String>("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);
}
src/batteries.rs (line 162)
157
158
159
160
161
162
163
164
165
166
167
pub fn cargo_helper<P, T>(cmd: &'static str, parser: P) -> impl Parser<T>
where
    P: Parser<T>,
{
    let skip = positional::<String>("cmd")
        .guard(move |s| s == cmd, "")
        .optional()
        .catch()
        .hide();
    construct!(skip, parser).map(|x| x.1)
}
src/lib.rs (line 1662)
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
pub fn cargo_helper<P, T>(cmd: &'static str, parser: P) -> impl Parser<T>
where
    T: 'static,
    P: Parser<T>,
{
    let skip = positional::<String>("cmd")
        .guard(move |s| s == cmd, "")
        .optional()
        .catch()
        .hide();
    construct!(skip, parser).map(|x| x.1)
}
examples/find.rs (line 32)
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
fn user() -> impl Parser<Option<String>> {
    let tag = any::<String>("TAG")
        .guard(|s| s == "-user", "not user")
        .hide();
    let value = positional::<String>("USER");
    construct!(tag, value)
        .anywhere()
        .map(|pair| pair.1)
        .optional()
        .catch()
}

// parsers -exec xxx yyy zzz ;
fn exec() -> impl Parser<Option<Vec<OsString>>> {
    let tag = any::<String>("-exec")
        .help("-exec /path/to/command flags and options ;")
        .guard(|s| s == "-exec", "not find");
    let item = any::<OsString>("ITEM")
        .guard(|s| s != ";", "not word")
        .many()
        .catch()
        .hide();
    let endtag = any::<String>("END").guard(|s| s == ";", "not eot").hide();
    construct!(tag, item, endtag)
        .anywhere()
        .map(|triple| triple.1)
        .optional()
        .catch()
}
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")
}

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
#[derive(Debug, Clone)]
pub struct Options {
    version: usize,
}
pub fn options() -> OptionParser<Options> {
    let version = long("version").argument("VERS").fallback(42);
    construct!(Options { version }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    #[bpaf(argument("VERS"), fallback(42))]
    version: usize,
}
Examples

Allows you to specify a default value used when argument is not specified

% app 
Options { version: 42 }

If value is present - fallback value is ignored

% app --version 10
Options { version: 10 }

Parsing errors are preserved and preserved to user

% app --version ten
Couldn't parse "ten": invalid digit found in string

bpaf encases parsers with fallback value in usage with []

% app --help
Usage: [--version VERS]

Available options:
        --version <VERS>
    -h, --help            Prints help information
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/dd.rs (line 32)
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
fn in_file() -> impl Parser<String> {
    tag::<String>("if=", "if=FILE", "read from FILE instead of stdin").fallback(String::from("-"))
}

fn out_file() -> impl Parser<String> {
    tag::<String>("of=", "of=FILE", "write to FILE instead of stdout").fallback(String::from("-"))
}

fn block_size() -> impl Parser<usize> {
    // it is possible to parse notation used by dd itself as well, using only ints for simplicity
    tag::<usize>(
        "bs=",
        "bs=SIZE",
        "read/write SIZE blocks at once instead of 512",
    )
    .fallback(512)
}
More examples
Hide additional examples
examples/top_to_bottom.rs (line 56)
51
52
53
54
55
56
57
fn speed() -> impl Parser<f64> {
    short('s')
        .long("speed")
        .help("Set speed")
        .argument::<f64>("SPEED")
        .fallback(42.0)
}
examples/ex_positional.rs (line 16)
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/xorg.rs (line 57)
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
pub fn options() -> OptionParser<Options> {
    let backing = toggle_options("backing", "Backing status").fallback(false);
    let xinerama = toggle_options("xinerama", "Xinerama status").fallback(true);
    let turbo = short('t')
        .long("turbo")
        .help("Engage the turbo mode")
        .switch();
    let extensions = extension().many();
    construct!(Options {
        turbo,
        backing,
        xinerama,
        extensions,
    })
    .to_options()
}
examples/cargo-cmd.rs (line 18)
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
fn main() {
    // defining a parser in a usual way
    let width = short('w').argument::<usize>("WIDTH").fallback(10);
    let height = short('h').argument::<usize>("HEIGHT").fallback(10);
    let parser = construct!(Opts { width, height });

    let cmd = positional::<String>("")
        .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/no_import.rs (lines 18-24)
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);
}

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 try_to_get_version() -> Result<usize, &'static str> {
    Ok(42)
}

#[derive(Debug, Clone)]
pub struct Options {
    version: usize,
}

pub fn options() -> OptionParser<Options> {
    let version = long("version")
        .argument("VERS")
        .fallback_with(try_to_get_version);
    construct!(Options { version }).to_options()
}
Derive usage
fn try_to_get_version() -> Result<usize, &'static str> {
    Ok(42)
}

#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    #[bpaf(argument("VERS"), fallback_with(try_to_get_version))]
    version: usize,
}
Examples

Allows you to specify a default value used when argument is not specified

% app 
Options { version: 42 }

If value is present - fallback value is ignored

% app --version 10
Options { version: 10 }

Parsing errors are preserved and preserved to user

% app --version ten
Couldn't parse "ten": invalid digit found in string

bpaf encases parsers with fallback value in usage with []

% app --help
Usage: [--version VERS]

Available options:
        --version <VERS>
    -h, --help            Prints help information
See also

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

Examples found in repository?
examples/derive_show_asm.rs (line 64)
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
fn parse_manifest_path() -> impl Parser<PathBuf> {
    long("manifest-path")
        .help("Path to Cargo.toml")
        .argument::<PathBuf>("PATH")
        .parse(|p| {
            if p.is_absolute() {
                Ok(p)
            } else {
                std::env::current_dir()
                    .map(|d| d.join(p))
                    .and_then(|full_path| full_path.canonicalize())
            }
        })
        .fallback_with(|| std::env::current_dir().map(|x| x.join("Cargo.toml")))
}

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

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

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

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

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

fn color_detection() -> impl Parser<bool> {
    let yes = long("color")
        .help("Enable color highlighting")
        .req_flag(true);
    let no = long("no-color")
        .help("Disable color highlighting")
        .req_flag(false);
    construct!([yes, no]).fallback_with::<_, &str>(|| Ok(true))
}

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
#[derive(Debug, Clone)]
pub struct Options {
    argument: u32,
    switch: bool,
}

pub fn options() -> OptionParser<Options> {
    let argument = long("argument")
        .help("important argument")
        .argument("ARG")
        .fallback(30);
    let switch = long("switch").help("secret switch").switch().hide();
    construct!(Options { argument, switch }).to_options()
}
Derive usage
#[allow(dead_code)]
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    /// important argument
    #[bpaf(fallback(30))]
    argument: u32,
    /// secret switch
    #[bpaf(hide)]
    switch: bool,
}
Examples

hide doesn’t change the parsing behavior in any way

% app --argument 32
Options { argument: 32, switch: false }

It hides the inner parser from any help or autocompletion logic

% app --help
Usage: [--argument ARG]

Available options:
        --argument <ARG>  important argument
    -h, --help            Prints help information
Examples found in repository?
src/batteries.rs (line 165)
157
158
159
160
161
162
163
164
165
166
167
pub fn cargo_helper<P, T>(cmd: &'static str, parser: P) -> impl Parser<T>
where
    P: Parser<T>,
{
    let skip = positional::<String>("cmd")
        .guard(move |s| s == cmd, "")
        .optional()
        .catch()
        .hide();
    construct!(skip, parser).map(|x| x.1)
}
More examples
Hide additional examples
src/lib.rs (line 1665)
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
pub fn cargo_helper<P, T>(cmd: &'static str, parser: P) -> impl Parser<T>
where
    T: 'static,
    P: Parser<T>,
{
    let skip = positional::<String>("cmd")
        .guard(move |s| s == cmd, "")
        .optional()
        .catch()
        .hide();
    construct!(skip, parser).map(|x| x.1)
}
examples/find.rs (line 33)
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
fn user() -> impl Parser<Option<String>> {
    let tag = any::<String>("TAG")
        .guard(|s| s == "-user", "not user")
        .hide();
    let value = positional::<String>("USER");
    construct!(tag, value)
        .anywhere()
        .map(|pair| pair.1)
        .optional()
        .catch()
}

// parsers -exec xxx yyy zzz ;
fn exec() -> impl Parser<Option<Vec<OsString>>> {
    let tag = any::<String>("-exec")
        .help("-exec /path/to/command flags and options ;")
        .guard(|s| s == "-exec", "not find");
    let item = any::<OsString>("ITEM")
        .guard(|s| s != ";", "not word")
        .many()
        .catch()
        .hide();
    let endtag = any::<String>("END").guard(|s| s == ";", "not eot").hide();
    construct!(tag, item, endtag)
        .anywhere()
        .map(|triple| triple.1)
        .optional()
        .catch()
}

/// parses symbolic permissions `-perm -mode`, `-perm /mode` and `-perm mode`
fn perm() -> impl Parser<Option<Perm>> {
    fn parse_mode(input: &str) -> Result<Perms, String> {
        let mut perms = Perms::default();
        for c in input.chars() {
            match c {
                'r' => perms.read = true,
                'w' => perms.write = true,
                'x' => perms.exec = true,
                _ => return Err(format!("{} is not a valid permission string", input)),
            }
        }
        Ok(perms)
    }

    let tag = any::<String>("-mode").help("-mode (perm | -perm | /perm)");
    let mode = any::<String>("mode")
        .parse::<_, _, String>(|s| {
            if let Some(m) = s.strip_prefix('-') {
                Ok(Perm::All(parse_mode(m)?))
            } else if let Some(m) = s.strip_prefix('/') {
                Ok(Perm::Any(parse_mode(m)?))
            } else {
                Ok(Perm::Exact(parse_mode(&s)?))
            }
        })
        .hide();

    construct!(tag, mode)
        .anywhere()
        .map(|pair| pair.1)
        .optional()
        .catch()
}
examples/cargo-cmd.rs (line 25)
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
fn main() {
    // defining a parser in a usual way
    let width = short('w').argument::<usize>("WIDTH").fallback(10);
    let height = short('h').argument::<usize>("HEIGHT").fallback(10);
    let parser = construct!(Opts { width, height });

    let cmd = positional::<String>("")
        .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/xorg.rs (line 52)
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
fn extension() -> impl Parser<(String, bool)> {
    let on = any::<String>("+ext")
        .help("enable ext <EXT>")
        .parse::<_, _, String>(|s| {
            if s == "+ext" {
                Ok(true)
            } else {
                Err(String::new())
            }
        });
    let off = any::<String>("-ext")
        .help("disable ext <EXT>")
        .parse::<_, _, String>(|s| {
            if s == "-ext" {
                Ok(false)
            } else {
                Err(String::new())
            }
        });

    let state = construct!([on, off]);
    let name = positional::<String>("EXT").hide();
    construct!(state, name).map(|(a, b)| (b, a)).anywhere()
}

Ignore this parser when generating usage line

Parsers hidden from usage will still show up in available arguments list. Best used on optional things that augment main application functionality but not define it. You might use custom usage to indicate that some options are hidden

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

pub fn options() -> OptionParser<Options> {
    let argument = long("argument")
        .help("important argument")
        .argument("ARG")
        .fallback(30);
    let switch = long("switch")
        .help("not that important switch")
        .switch()
        .hide_usage();
    construct!(Options { argument, switch }).to_options()
}
Derive usage
#[allow(dead_code)]
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    /// important argument
    #[bpaf(fallback(30))]
    argument: u32,
    /// not that important switch
    #[bpaf(hide_usage)]
    switch: bool,
}
Examples

hide_usage doesn’t change the parsing behavior in any way

% app --argument 32
Options { argument: 32, switch: false }

It hides the inner parser from usage line, but not from the rest of the help or completion

% app --help
Usage: [--argument ARG]

Available options:
        --argument <ARG>  important argument
        --switch          not that important switch
    -h, --help            Prints help information

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
#[derive(Debug, Clone)]
pub struct Rectangle {
    width: u32,
    height: u32,
}

#[derive(Debug, Clone)]
pub struct Options {
    argument: u32,
    rectangle: Rectangle,
}

pub fn options() -> OptionParser<Options> {
    let argument = long("argument")
        .help("important argument")
        .argument("ARG")
        .fallback(30);

    let width = long("width")
        .help("Width of the rectangle")
        .argument("W")
        .fallback(10);
    let height = long("height")
        .help("Height of the rectangle")
        .argument("H")
        .fallback(10);
    let rectangle = construct!(Rectangle { width, height }).group_help("takes a rectangle");

    construct!(Options {
        argument,
        rectangle
    })
    .to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
pub struct Rectangle {
    /// Width of the rectangle
    #[bpaf(argument("W"), fallback(10))]
    width: u32,
    /// Height of the rectangle
    #[bpaf(argument("H"), fallback(10))]
    height: u32,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    /// important argument
    #[bpaf(fallback(30))]
    argument: u32,
    /// secret switch
    #[bpaf(external, group_help("takes a rectangle"))]
    rectangle: Rectangle,
}
Examples

group_help doesn’t change the parsing behavior in any way

% app --argument 32 --width 20 --height 13
Options { argument: 32, rectangle: Rectangle { width: 20, height: 13 } }

Instead it adds extra decoration for the inner group in –help message

% app --help
Usage: [--argument ARG] [--width W] [--height H]

Available options:
        --argument <ARG>  important argument
  takes a rectangle
        --width <W>       Width of the rectangle
        --height <H>      Height of the rectangle

    -h, --help            Prints help information
Examples found in repository?
examples/rectangle.rs (line 31)
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
fn main() {
    let width = short('w')
        .long("width")
        .help("Width of the rectangle")
        .argument::<usize>("PX");

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

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

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

Dynamic shell completion

Allows to generate autocompletion information for shell. Completer places generated input in place of metavar placeholders, so running completer on something that doesn’t have a positional or an argument doesn’t make much sense.

Takes a function as a parameter that tries to complete partial input to a full one with optional description. bpaf would substitute current positional item or an argument an empty string if a value isn’t available yet so it’s best to run complete where parsing can’t fail: right after argument or positional, but this isn’t enforced.

bpaf doesn’t support generating OsString completions: bpaf must print completions to console and for non-string values it’s not possible (accurately).

Using this function requires enabling "autocomplete" feature, not enabled by default.

Example
$ app --name L<TAB>
$ app --name Lupusregina _
Combinatoric usage
fn completer(input: &String) -> Vec<(&'static str, Option<&'static str>)> {
    let names = ["Yuri", "Lupusregina", "Solution", "Shizu", "Entoma"];
    names
        .iter()
        .filter(|name| name.starts_with(input))
        .map(|name| (*name, None))
        .collect::<Vec<_>>()
}

fn name() -> impl Parser<String> {
    short('n')
        .long("name")
        .help("Specify character's name")
        .argument::<String>("Name")
        .complete(completer)
}
Derive usage
fn completer(input: &String) -> Vec<(&'static str, Option<&'static str>)> {
    let names = ["Yuri", "Lupusregina", "Solution", "Shizu", "Entoma"];
    names
        .iter()
        .filter(|name| name.starts_with(input))
        .map(|name| (*name, None))
        .collect::<Vec<_>>()
}

#[derive(Debug, Clone, Bpaf)]
struct Options {
    #[bpaf(argument("NAME"), complete(completer))]
    name: String,
}
Examples found in repository?
examples/sensors.rs (line 59)
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()
}
More examples
Hide additional examples
examples/csample.rs (line 19)
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
fn main() {
    use bpaf::*;

    let a = short('a').long("avocado").help("Use avocado").switch();
    let b = short('b').long("banana").help("Use banana").switch();
    let bb = long("bananananana").help("I'm Batman").switch();
    let c = long("calculator")
        .help("calculator expression")
        .argument::<String>("EXPR")
        .complete(complete_calculator);
    let parser = construct!(a, b, bb, c)
        .to_options()
        .descr("Dynamic autocomplete example")
        .footer(
            "\
    bpaf supports dynamic autocompletion for a few shells, make sure your binary is in $PATH
    and try using one of those this output should go into a file that depends on your shell:
    $ csample --bpaf-complete-style-bash
    $ csample --bpaf-complete-style-zsh
    $ csample --bpaf-complete-style-fish
    $ csample --bpaf-complete-style-elvish",
        );

    println!("{:?}", parser.run());
}

Static shell completion

Allows to ask existing shell completion to provide some information such as file or directory names or pass though existing shell completion scripts, see ShellComp for accessible functionality

Places function call in place of metavar placeholder, so running complete_shell on something that doesn’t have a positional or argument doesn’t make much sense.

Using this function requires enabling "autocomplete" feature, not enabled by default.

Example
$ app --output C<TAB>
$ app --output Cargo.toml _
Combinatoric usage
fn output() -> impl Parser<String> {
    long("output")
        .help("Cargo.toml file to use as output")
        .argument("OUTPUT")
        .complete_shell(ShellComp::File { mask: Some("*.toml") })
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
struct Options {
    /// Cargo.toml file to use as output
    #[bpaf(argument("OUTPUT"), complete_shell(ShellComp::File { mask: Some("*.toml") }))]
    output: String,
}
Examples found in repository?
examples/basic.rs (line 46)
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
73
fn opts() -> OptionParser<Out> {
    // A flag, true if used in the command line. Can be required, this one is optional

    let debug = short('d') // start with a short name
        .long("debug") // also add a long name
        .help("Activate debug mode") // and a help message to use
        .switch(); // turn this into a 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::<f64>("SPEED") // you can specify a type to parse
        .fallback(42.0);

    let output = short('o')
        .long("output")
        .help("output file")
        .argument::<PathBuf>("OUTPUT") // but it's optional when rustc can derive it
        .complete_shell(ShellComp::File { mask: None });

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

    // a parser that consumes one argument
    // you can build the inner parser in one go or as multiple steps giving each step a name
    // you can also add some static shell completion functionality
    let file_to_proces = short('f')
        .long("file")
        .help("File to process")
        .argument::<PathBuf>("FILE")
        .complete_shell(ShellComp::File { mask: Some("*.rs") });

    let files_to_process = file_to_proces.many();

    // packing things in a struct assumes parser for each field is in scope.
    construct!(Out {
        debug,
        verbose,
        speed,
        output,
        nb_cars,
        files_to_process
    })
    .to_options()
    .descr("This is a description")
}

Add extra annotations to completion information

Not all information is gets supported by all the shells

Combinatoric usage
fn opts() -> impl Parser<(bool, bool)> {
    let a = short('a').switch();
    let b = short('b').switch();
    let c = short('c').switch();
    let d = short('d').switch();
    let ab = construct!(a, b).complete_style(CompleteDecor::VisibleGroup("a and b"));
    let cd = construct!(c, d).complete_style(CompleteDecor::VisibleGroup("c and d"));
    construct!([ab, cd])
}

Automagically restrict the inner parser scope to accept adjacent values only

adjacent can solve surprisingly wide variety of problems: sequential command chaining, multi-value arguments, option-structs to name a few. If you want to run a parser on a sequential subset of arguments - adjacent might be able to help you. Check the examples for better intuition.

Multi-value arguments

Parsing things like --foo ARG1 ARG2 ARG3

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

#[derive(Debug, Clone)]
struct Multi {
    m: (),
    val_1: usize,
    val_2: usize,
    val_3: f64,
}

fn multi() -> impl Parser<Multi> {
    let m = short('m').req_flag(());
    let val_1 = positional::<usize>("V1");
    let val_2 = positional::<usize>("V2");
    let val_3 = positional::<f64>("V3");
    construct!(Multi {
        m,
        val_1,
        val_2,
        val_3
    })
    .adjacent()
}

pub fn options() -> OptionParser<Options> {
    let switch = short('s').switch();
    let multi = multi().many();
    construct!(Options { multi, switch }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    #[bpaf(external, many)]
    multi: Vec<Multi>,
    #[bpaf(short)]
    switch: bool,
}

#[derive(Debug, Clone, Bpaf)]
#[bpaf(adjacent)]
struct Multi {
    m: (),
    #[bpaf(positional("V1"))]
    val_1: usize,
    #[bpaf(positional("V2"))]
    val_2: usize,
    #[bpaf(positional("V3"))]
    val_3: f64,
}
Examples

short flag -m takes 3 positional arguments: two integers and one floating point, order is important, switch -s can go on either side of it

% app -s -m 10 20 3.1415
Options { multi: [Multi { m: (), val_1: 10, val_2: 20, val_3: 3.1415 }], switch: true }

parser accepts multiple groups of -m - they must not interleave

% app -s -m 10 20 3.1415 -m 1 2 0.0
Options { multi: [Multi { m: (), val_1: 10, val_2: 20, val_3: 3.1415 }, Multi { m: (), val_1: 1, val_2: 2, val_3: 0.0 }], switch: true }

-s can’t go in the middle as the parser expects the second item

% app -m 10 20 -s 3.1415
Expected <V3>, pass --help for usage information
Structure groups

Parsing things like --foo --foo-1 ARG1 --foo-2 ARG2 --foo-3 ARG3

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

#[derive(Debug, Clone)]
struct Rect {
    item: (),
    width: usize,
    height: usize,
    painted: bool,
}

fn multi() -> impl Parser<Rect> {
    let item = long("rect").req_flag(());
    let width = long("width").argument::<usize>("PX");
    let height = long("height").argument::<usize>("PX");
    let painted = long("painted").switch();
    construct!(Rect {
        item,
        width,
        height,
        painted,
    })
    .adjacent()
}

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

Order of items within the rectangle is not significant and you can have several of them

% app --rect --width 10 --height 10 --rect --height 10 --width 10
Options { switch: false, multi: [Rect { item: (), width: 10, height: 10, painted: false }, Rect { item: (), width: 10, height: 10, painted: false }] }

You can have optional values that belong to the group inside and outer flags in the middle

% app --rect --width 10 --painted --height 10 -s --rect --height 10 --width 10
Options { switch: true, multi: [Rect { item: (), width: 10, height: 10, painted: true }, Rect { item: (), width: 10, height: 10, painted: false }] }

But with adjacent they cannot interleave

% app --rect --rect --width 10 --painted --height 10 --height 10 --width 10
Expected --width PX, pass --help for usage information
Chaining commands

Parsing things like cmd1 --arg1 cmd2 --arg2 --arg3 cmd3 --flag

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

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

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

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

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

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

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

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

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

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

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

Parsing things like find . --exec foo {} -bar ; --more

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

fn exec() -> impl Parser<Vec<OsString>> {
    let start = long("exec").req_flag(());
    let body = any::<OsString>("EXEC")
        .guard(|s| s != ";", "end marker")
        .many()
        .catch();
    let end = any::<OsString>("TAIL").guard(|s| s == ";", "end marker");
    construct!(start, body, end).adjacent().map(|x| x.1)
}

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

You can have as many items between --exec and ; as you want, they all will be captured inside the exec vector. Extra options can go either before or after the block.

% app --exec foo --bar ; -s
Options { switch: true, exec: ["foo", "--bar"] }
Multi-value arguments with optional flags

Parsing things like --foo ARG1 --flag --inner ARG2

So you can parse things while parsing things. Not sure why you might need this, but you can :)

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

#[derive(Debug, Clone)]
struct Multi {
    m: (),
    pos: usize,
    flag: bool,
    arg: Option<usize>,
}

/// You can mix all sorts of things inside the adjacent group
fn multi() -> impl Parser<Multi> {
    let m = short('m').req_flag(());
    let pos = positional::<usize>("POS");
    let arg = long("arg").argument::<usize>("ARG").optional();
    let flag = long("flag").switch();
    construct!(Multi { m, arg, flag, pos }).adjacent()
}

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

Let’s start simple - a single flag accepts a bunch of stuff, and eveything is present

% app -m 330 --arg 10 --flag
Options { multi: [Multi { m: (), pos: 330, flag: true, arg: Some(10) }], switch: false }

You can omit some parts, but also have multiple groups thank to many

% app -m 100 --flag    -m 30 --arg 10    -m 50
Options { multi: [Multi { m: (), pos: 100, flag: true, arg: None }, Multi { m: (), pos: 30, flag: false, arg: Some(10) }, Multi { m: (), pos: 50, flag: false, arg: None }], switch: false }
Performance and other considerations

bpaf can run adjacently restricted parsers multiple times to refine the guesses. It’s best not to have complex inter-fields verification since they might trip up the detection logic: instead of destricting, for example “sum of two fields to be 5 or greater” inside the adjacent parser, you can restrict it outside, once adjacent done the parsing.

adjacent is available on a trait for better discoverability, it doesn’t make much sense to use it on something other than command or construct! encasing several fields.

There’s also similar method adjacent that allows to restrict argument parser to work only for arguments where both key and a value are in the same shell word: -f=bar or -fbar, but not -f bar.

Examples found in repository?
examples/sensors.rs (line 72)
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()
}

Parse anywhere

Most generic escape hatch available, in combination with any allows to parse anything anywhere, works by repeatedly trying to run the inner parser on each subsequent context. Can be expensive performance wise especially if parser contains complex logic.

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

fn toggle_options(name: &'static str, help: &'static str) -> impl Parser<bool> {
    any::<String>(name)
        .help(help)
        .parse(move |s| {
            let (state, cur_name) = if let Some(rest) = s.strip_prefix('+') {
                (true, rest)
            } else if let Some(rest) = s.strip_prefix('-') {
                (false, rest)
            } else {
                return Err(format!("{} is not a toggle option", s));
            };
            if cur_name != name {
                Err(format!("{} is not a known toggle option name", cur_name))
            } else {
                Ok(state)
            }
        })
        .anywhere()
}

pub fn options() -> OptionParser<Options> {
    let backing = toggle_options("backing", "Backing status").fallback(false);
    let xinerama = toggle_options("xinerama", "Xinerama status").fallback(true);
    let turbo = short('t')
        .long("turbo")
        .help("Engage the turbo mode")
        .switch();
    construct!(Options {
        turbo,
        backing,
        xinerama,
    })
    .to_options()
}
Examples

This example shows how to parse some very unusual options, same style as used by Xorg -backing disables backing +backing enables it, usual restrictions and combinations apply: fails if present more than once, can be further transformed with combinators. By default xinerama is enabled, anything else is disabled

% app 
Options { turbo: false, backing: false, xinerama: true }

Strange things we added can be mixed with the regular options

% app --turbo +backing -xinerama
Options { turbo: true, backing: true, xinerama: false }

As expected - order doesn’t matter

% app +backing --turbo
Options { turbo: true, backing: true, xinerama: true }

–help will try to render it but you can always .hide it and add your own lines with .header or .footer methods on OptionParser.

% app --help
Usage: [-t] [<backing>] [<xinerama>]

Available positional items:
    <backing>   Backing status
    <xinerama>  Xinerama status

Available options:
    -t, --turbo  Engage the turbo mode
    -h, --help   Prints help information
Examples found in repository?
examples/find.rs (line 36)
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
fn user() -> impl Parser<Option<String>> {
    let tag = any::<String>("TAG")
        .guard(|s| s == "-user", "not user")
        .hide();
    let value = positional::<String>("USER");
    construct!(tag, value)
        .anywhere()
        .map(|pair| pair.1)
        .optional()
        .catch()
}

// parsers -exec xxx yyy zzz ;
fn exec() -> impl Parser<Option<Vec<OsString>>> {
    let tag = any::<String>("-exec")
        .help("-exec /path/to/command flags and options ;")
        .guard(|s| s == "-exec", "not find");
    let item = any::<OsString>("ITEM")
        .guard(|s| s != ";", "not word")
        .many()
        .catch()
        .hide();
    let endtag = any::<String>("END").guard(|s| s == ";", "not eot").hide();
    construct!(tag, item, endtag)
        .anywhere()
        .map(|triple| triple.1)
        .optional()
        .catch()
}

/// parses symbolic permissions `-perm -mode`, `-perm /mode` and `-perm mode`
fn perm() -> impl Parser<Option<Perm>> {
    fn parse_mode(input: &str) -> Result<Perms, String> {
        let mut perms = Perms::default();
        for c in input.chars() {
            match c {
                'r' => perms.read = true,
                'w' => perms.write = true,
                'x' => perms.exec = true,
                _ => return Err(format!("{} is not a valid permission string", input)),
            }
        }
        Ok(perms)
    }

    let tag = any::<String>("-mode").help("-mode (perm | -perm | /perm)");
    let mode = any::<String>("mode")
        .parse::<_, _, String>(|s| {
            if let Some(m) = s.strip_prefix('-') {
                Ok(Perm::All(parse_mode(m)?))
            } else if let Some(m) = s.strip_prefix('/') {
                Ok(Perm::Any(parse_mode(m)?))
            } else {
                Ok(Perm::Exact(parse_mode(&s)?))
            }
        })
        .hide();

    construct!(tag, mode)
        .anywhere()
        .map(|pair| pair.1)
        .optional()
        .catch()
}
More examples
Hide additional examples
examples/dd.rs (line 28)
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
fn tag<T>(name: &'static str, meta: &'static str, help: &'static str) -> impl Parser<T>
where
    T: FromStr,
    <T as std::str::FromStr>::Err: std::fmt::Display,
{
    // it is possible to parse OsString here and strip the prefix with os_str_bytes or a similar
    // crate
    any::<String>(meta)
        .help(help)
        .parse::<_, _, String>(move |s| match s.strip_prefix(name) {
            None => Err("Wrong tag".to_string()),
            Some(body) => T::from_str(body).map_err(|e| e.to_string()),
        })
        .anywhere()
}
examples/xorg.rs (line 28)
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
fn toggle_options(name: &'static str, help: &'static str) -> impl Parser<bool> {
    any::<String>(name)
        .help(help)
        .parse(move |s| {
            let (state, cur_name) = if let Some(rest) = s.strip_prefix('+') {
                (true, rest)
            } else if let Some(rest) = s.strip_prefix('-') {
                (false, rest)
            } else {
                return Err(format!("{} is not a toggle option", s));
            };
            if cur_name != name {
                Err(format!("{} is not a known toggle option name", cur_name))
            } else {
                Ok(state)
            }
        })
        .anywhere()
}

fn extension() -> impl Parser<(String, bool)> {
    let on = any::<String>("+ext")
        .help("enable ext <EXT>")
        .parse::<_, _, String>(|s| {
            if s == "+ext" {
                Ok(true)
            } else {
                Err(String::new())
            }
        });
    let off = any::<String>("-ext")
        .help("disable ext <EXT>")
        .parse::<_, _, String>(|s| {
            if s == "-ext" {
                Ok(false)
            } else {
                Err(String::new())
            }
        });

    let state = construct!([on, off]);
    let name = positional::<String>("EXT").hide();
    construct!(state, name).map(|(a, b)| (b, a)).anywhere()
}

Transform Parser into OptionParser to attach metadata and run

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

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?
src/lib.rs (line 1479)
1475
1476
1477
1478
1479
1480
    fn run(self) -> T
    where
        Self: Sized + Parser<T> + 'static,
    {
        self.to_options().run()
    }
More examples
Hide additional examples
examples/enum_in_args.rs (line 33)
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/dd.rs (line 57)
49
50
51
52
53
54
55
56
57
58
pub fn options() -> OptionParser<Options> {
    let magic = long("magic").switch();
    construct!(Options {
        magic,
        in_file(),
        out_file(),
        block_size(),
    })
    .to_options()
}
examples/find.rs (line 104)
95
96
97
98
99
100
101
102
103
104
105
pub fn options() -> OptionParser<Options> {
    let paths = positional::<PathBuf>("PATH").many();

    construct!(Options {
        exec(),
        user(),
        perm(),
        paths,
    })
    .to_options()
}
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);
}
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::<String>("KEY");

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

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

Implementors§