Struct bpaf::OptionParser

source ·
pub struct OptionParser<T> { /* private fields */ }
Expand description

Ready to run Parser with additional information attached

Created with to_options

Implementations§

Execute the OptionParser, extract a parsed value or print some diagnostic and exit

Usage
/// Parses number of repetitions of `-v` on a command line
fn verbosity() -> OptionParser<usize> {
    let parser = short('v')
        .req_flag(())
        .many()
        .map(|xs|xs.len());

    parser
        .to_options()
        .descr("Takes verbosity flag and does nothing else")
}

fn main() {
    let verbosity: usize = verbosity().run();
}
Examples found in repository?
examples/derive_this_or_that.rs (line 23)
22
23
24
fn main() {
    println!("{:?}", opts().run())
}
More examples
Hide additional examples
examples/derive.rs (line 43)
42
43
44
fn main() {
    println!("{:#?}", opts().run());
}
examples/derive_commands.rs (line 50)
49
50
51
fn main() {
    println!("{:?}", action().run());
}
examples/derive_rudo.rs (line 35)
34
35
36
fn main() {
    println!("{:?}", options().run());
}
examples/xorg.rs (line 74)
73
74
75
fn main() {
    println!("{:#?}", options().run());
}
examples/numeric_prefix.rs (line 21)
20
21
22
fn main() {
    println!("{:#?}", options().run());
}

Execute the OptionParser, extract a parsed value or return a ParseFailure

In most cases using run is sufficient, you can use try_run if you want to control the exit code or you need to perform a custom cleanup.

Usage
/// Parses number of repetitions of `-v` on a command line
fn verbosity() -> OptionParser<usize> {
    let parser = short('v')
        .req_flag(())
        .many()
        .map(|xs|xs.len());

    parser
        .to_options()
        .descr("Takes verbosity flag and does nothing else")
}

fn main() {
    let verbosity: Option<usize> = match verbosity().try_run() {
        Ok(v) => Some(v),
        Err(ParseFailure::Stdout(msg)) => {
            print!("{}", msg); // completions are sad otherwise
            None
        }
        Err(ParseFailure::Stderr(msg)) => {
            eprintln!("{}", msg);
            None
        }
    };

    // Run cleanup tasks
}
Errors

ParseFailure represents parsing errors, autocomplete results and generated --help output.

Examples found in repository?
src/info.rs (line 133)
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
    pub fn run(self) -> T
    where
        Self: Sized,
    {
        match self.try_run() {
            Ok(t) => t,
            Err(ParseFailure::Stdout(msg)) => {
                print!("{}", msg); // completions are sad otherwise
                std::process::exit(0);
            }
            Err(ParseFailure::Stderr(msg)) => {
                eprintln!("{}", msg);
                std::process::exit(1);
            }
        }
    }

Execute the OptionParser and produce a values for unit tests or manual processing

#[test]
fn positional_argument() {
    let parser =
        positional::<String>("FILE")
            .help("File to process")
            .to_options();

    let help = parser
        .run_inner(Args::from(&["--help"]))
        .unwrap_err()
        .unwrap_stdout();
    let expected_help = "\
Usage: <FILE>

Available positional items:
    <FILE>  File to process

Available options:
    -h, --help  Prints help information
";
    assert_eq!(expected_help, help);
}

See also Args and it’s From impls to produce input and ParseFailure::unwrap_stderr / ParseFailure::unwrap_stdout for processing results.

Errors

If parser can’t produce desired result run_inner returns ParseFailure which represents runtime behavior: one branch to print something to stdout and exit with success and the other branch to print something to stderr and exit with failure.

bpaf generates contents of this ParseFailure using expected textual output from parse, stdout/stderr isn’t actually captured.

Exact string reperentations may change between versions including minor releases.

Examples found in repository?
src/info.rs (line 219)
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
    pub fn try_run(self) -> Result<T, ParseFailure>
    where
        Self: Sized,
    {
        let mut arg_vec = Vec::new();
        #[cfg(feature = "autocomplete")]
        let mut complete_vec = Vec::new();

        let mut args = std::env::args_os();

        #[allow(unused_variables)]
        let name = args.next().expect("no command name from args_os?");

        #[cfg(feature = "autocomplete")]
        for arg in args {
            if arg
                .to_str()
                .map_or(false, |s| s.starts_with("--bpaf-complete-"))
            {
                complete_vec.push(arg);
            } else {
                arg_vec.push(arg);
            }
        }
        #[cfg(not(feature = "autocomplete"))]
        arg_vec.extend(args);

        #[cfg(feature = "autocomplete")]
        let args = crate::complete_run::args_with_complete(name, &arg_vec, &complete_vec);
        #[cfg(not(feature = "autocomplete"))]
        let args = Args::from(arg_vec.as_slice());

        self.run_inner(args)
    }
More examples
Hide additional examples
src/complete_run.rs (line 210)
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
pub(crate) fn args_with_complete(
    os_name: OsString,
    arguments: &[OsString],
    complete_arguments: &[OsString],
) -> Args {
    let path = PathBuf::from(os_name);
    let path = path.file_name().expect("binary with no name?").to_str();

    // not trying to run a completer - just make the arguments
    if complete_arguments.is_empty() {
        return Args::from(arguments);
    }

    let cargs = Args::from(complete_arguments);

    match parse_comp_options().run_inner(cargs) {
        Ok(comp) => {
            let name = match path {
                Some(path) => path,
                None => panic!("app name is not utf8, giving up rendering completer"),
            };

            let rev = match comp {
                CompOptions::Dump { style } => {
                    match style {
                        Style::Bash => dump_bash_completer(name),
                        Style::Zsh => dump_zsh_completer(name),
                        Style::Fish => dump_fish_completer(name),
                        Style::Elvish => dump_elvish_completer(name),
                    }
                    std::process::exit(0)
                }
                CompOptions::Complete { revision } => revision,
            };
            Args::from(arguments).set_comp(rev)
        }

        Err(err) => {
            eprintln!("Can't parse bpaf complete options: {:?}", err);
            std::process::exit(1);
        }
    }
}

Set the version field.

By default bpaf won’t include any version info and won’t accept --version switch.

Combinatoric usage
use bpaf::*;
fn options() -> OptionParser<bool>  {
   short('s')
       .switch()
       .to_options()
       .version(env!("CARGO_PKG_VERSION"))
}
Derive usage

version annotation is available after options and command annotations, takes an optional argument - version value to use, otherwise bpaf_derive would use value from cargo.

#[derive(Debug, Clone, Bpaf)]
#[bpaf(options, version)]
struct Options {
    #[bpaf(short)]
    switch: bool
}
Example
$ app --version
Version: 0.5.0

Set the description field

Description field should be 1-2 lines long briefly explaining program purpose. If description field is present bpaf would print it right before the usage line.

Combinatoric usage
fn options() -> OptionParser<bool>  {
   short('s')
       .switch()
       .to_options()
       .descr("This is a description")
       .header("This is a header")
       .footer("This is a footer")
}
Derive usage

bpaf_derive uses doc comments on the struct / enum to derive description, it skips single empty lines and uses double empty lines break it into blocks. bpaf_derive would use first block as the description, second block - header, third block - footer.

#[derive(Debug, Clone, Bpaf)]
#[bpaf(options, version)]
/// This is a description
///
///
/// This is a header
///
///
/// This is a footer
///
///
/// This is just a comment
struct Options {
    #[bpaf(short)]
    switch: bool
}
Example
This is a description

Usage: [-s]

This is a header

Available options:
    -s
    -h, --help     Prints help information
    -V, --version  Prints version information

This is a footer
Examples found in repository?
examples/customize_help.rs (line 11)
6
7
8
9
10
11
12
13
14
15
16
17
18
fn main() {
    let opt = short('d')
        .help("Release the dragon")
        .switch()
        .to_options()
        .descr("I am a program and I do things")
        .header("Sometimes they even work.")
        .footer("Beware `-d`, dragons be here")
        .usage("You can call it with following flags: {usage}")
        .run();

    println!("{:?}", opt);
}
More examples
Hide additional examples
examples/enum_tuple.rs (line 24)
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
fn main() {
    let bar = short('b')
        .long("bar")
        .help("some bar command")
        .argument::<String>("BAR")
        .optional();

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

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

    println!("{:#?}", opt);
}
examples/negative.rs (line 15)
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
    let age = long("age").argument::<i64>("AGE");
    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/cat.rs (line 31)
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/rectangle.rs (line 41)
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);
}
examples/git.rs (line 36)
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
fn main() {
    let dry_run = long("dry_run").switch();
    let all = long("all").switch();
    let repository = positional::<String>("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::<PathBuf>("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);
}

Set the header field

bpaf displays the header between the usage line and a list of the available options in --help output

Combinatoric usage
fn options() -> OptionParser<bool>  {
   short('s')
       .switch()
       .to_options()
       .descr("This is a description")
       .header("This is a header")
       .footer("This is a footer")
}
Derive usage

bpaf_derive uses doc comments on the struct / enum to derive description, it skips single empty lines and uses double empty lines break it into blocks. bpaf_derive would use first block as the description, second block - header, third block - footer.

#[derive(Debug, Clone, Bpaf)]
#[bpaf(options, version)]
/// This is a description
///
///
/// This is a header
///
///
/// This is a footer
///
///
/// This is just a comment
struct Options {
    #[bpaf(short)]
    switch: bool
}
Example
This is a description

Usage: [-s]

This is a header

Available options:
    -s
    -h, --help     Prints help information
    -V, --version  Prints version information

This is a footer
Examples found in repository?
examples/customize_help.rs (line 12)
6
7
8
9
10
11
12
13
14
15
16
17
18
fn main() {
    let opt = short('d')
        .help("Release the dragon")
        .switch()
        .to_options()
        .descr("I am a program and I do things")
        .header("Sometimes they even work.")
        .footer("Beware `-d`, dragons be here")
        .usage("You can call it with following flags: {usage}")
        .run();

    println!("{:?}", opt);
}
More examples
Hide additional examples
examples/rectangle.rs (line 42)
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);
}
examples/travel.rs (line 68)
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
fn main() {
    // Suppose we want to calcualte a total time of a travel, where parts of
    // a travel can be given either as pairs of speed and distance or just by time.
    // Speed can be given by KPH or MPH. Distance - either miles or km.

    // parsers for speeds. Both speeds are converted to the same units
    let mph = long("mph")
        .help("speed in MPH")
        .argument::<f64>("SPEED")
        .map(|x| x * 1.6);
    let kph = long("kph").help("Speed in KPH").argument::<f64>("SPEED");

    // speed is either kph or mph, conversion to mph is handled by the parser
    let speed = construct!([mph, kph]);

    // parsers for distances, both are converted to the same units
    let km = long("km").help("Distance in KM").argument::<f64>("KMs");
    let mi = long("mi")
        .help("distance in miles")
        .argument::<f64>("MILES")
        .map(|x| x * 1.6);
    let dist = construct!([mi, km]);

    // time, presumably in seconds
    let time = long("time")
        .help("Travel time in hours")
        .argument::<f64>("TIME");

    // parsed time is trivially converted to time segment
    let segment_time = time.map(Segment::Time);

    // parsed speed/distance is packed into SpeedDistance segment
    let segment_speed = construct!(Segment::SpeedDistance { speed, dist });

    // segment can be either of two defined
    let segment = construct!([segment_speed, segment_time]);

    // and we have several of them.
    let parser = segment
        .many()
        .guard(|x| !x.is_empty(), "need at least one segment")
        .guard(
            |x| x.len() < 10,
            "for more than 9 segments you need to purchase a premium subscription",
        );

    let descr = "Accepts one or more travel segments";
    let header = "You need to specify one or more travel segments, segment is defined by
a pair of speed and distance or by time.

This example defines two separate travel segments, one given by speed/distance combo and one by time
    travel --km 180 --kph 35 --time";
    let decorated = parser.to_options().descr(descr).header(header);

    // help message tries to explain what's needed:
    // either --time OR one speed and one distance, both can be given in miles or km.
    // number of speed flags must correspond to number of distance flags, more or
    // less results in parser error messages
    let opt = decorated.run();

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

Set the footer field

bpaf displays the footer after list of the available options in --help output

Combinatoric usage
fn options() -> OptionParser<bool>  {
   short('s')
       .switch()
       .to_options()
       .descr("This is a description")
       .header("This is a header")
       .footer("This is a footer")
}
Derive usage

bpaf_derive uses doc comments on the struct / enum to derive description, it skips single empty lines and uses double empty lines break it into blocks. bpaf_derive would use first block as the description, second block - header, third block - footer.

#[derive(Debug, Clone, Bpaf)]
#[bpaf(options, version)]
/// This is a description
///
///
/// This is a header
///
///
/// This is a footer
///
///
/// This is just a comment
struct Options {
    #[bpaf(short)]
    switch: bool
}
Example
This is a description

Usage: [-s]

This is a header

Available options:
    -s
    -h, --help     Prints help information
    -V, --version  Prints version information

This is a footer
Examples found in repository?
examples/customize_help.rs (line 13)
6
7
8
9
10
11
12
13
14
15
16
17
18
fn main() {
    let opt = short('d')
        .help("Release the dragon")
        .switch()
        .to_options()
        .descr("I am a program and I do things")
        .header("Sometimes they even work.")
        .footer("Beware `-d`, dragons be here")
        .usage("You can call it with following flags: {usage}")
        .run();

    println!("{:?}", opt);
}
More examples
Hide additional examples
examples/rectangle.rs (line 43)
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);
}
examples/csample.rs (lines 23-31)
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());
}

Set custom usage field

Custom usage field to use instead of one derived by bpaf. Custom message should contain "Usage: " prefix if you want to display one.

Before using it bpaf would replace "{usage}" tokens inside a custom usage string with automatically generated usage.

Combinatoric usage
fn options() -> OptionParser<bool>  {
   short('s')
       .switch()
       .to_options()
       .usage("Usage: my_program: {usage}")
}
Derive usage

Not available directly, but you can call usage on generated OptionParser.

Examples found in repository?
examples/customize_help.rs (line 14)
6
7
8
9
10
11
12
13
14
15
16
17
18
fn main() {
    let opt = short('d')
        .help("Release the dragon")
        .switch()
        .to_options()
        .descr("I am a program and I do things")
        .header("Sometimes they even work.")
        .footer("Beware `-d`, dragons be here")
        .usage("You can call it with following flags: {usage}")
        .run();

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

Turn OptionParser into subcommand parser

This is identical to command

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

Check the invariants bpaf relies on for normal operations

Takes a parameter whether to check for cosmetic invariants or not (max help width exceeding 120 symbols, etc), currently not in use

Best used as part of your test suite:

#[test]
fn check_options() {
    options().check_invariants(false)
}
Panics

check_invariants indicates problems with panic

Auto Trait Implementations§

Blanket Implementations§

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

Returns the argument unchanged.

Calls U::from(self).

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

The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.