Struct bpaf::OptionParser

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

Ready to run Parser with additional information attached

Created with to_options

In addition to the inner parser OptionParser contains documentation about a program or a subcommand as a whole, version, custom usage, if specified, and handles custom parsers for --version and --help flags.

Implementations§

source§

impl<T> OptionParser<T>

source

pub fn render_html(&self, app: impl Into<String>) -> String

Available on crate feature docgen only.

Render command line documentation for the app into html/markdown mix

source

pub fn render_markdown(&self, app: impl Into<String>) -> String

Available on crate feature docgen only.

Render command line documentation for the app into Markdown

source§

impl<T> OptionParser<T>

source

pub fn render_manpage( &self, app: impl AsRef<str>, section: Section<'_>, last_update_date: Option<&str>, vendor: Option<&str>, application_title: Option<&str> ) -> String

Available on crate feature docgen only.

Render command line documentation for the app into a manpage

source§

impl<T> OptionParser<T>

source

pub fn run(self) -> T
where Self: Sized,

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

pub fn try_run(self) -> Result<T, ParseFailure>
where Self: Sized,

👎Deprecated: You should switch to equivalent parser.run_inner(Args::current_args())

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(buf, full)) => {
            print!("{}", buf.monochrome(full));
            None
        }
        Err(ParseFailure::Completion(msg)) => {
            print!("{}", msg);
            None
        }
        Err(ParseFailure::Stderr(buf)) => {
            eprintln!("{}", buf.monochrome(true));
            None
        }
    };

    // Run cleanup tasks
}
§Errors

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

source

pub fn run_inner<'a>( &self, args: impl Into<Args<'a>> ) -> Result<T, ParseFailure>
where Self: Sized,

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(&["--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.

source

pub fn version<B: Into<Doc>>(self, version: B) -> Self

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
source

pub fn descr<B: Into<Doc>>(self, descr: B) -> Self

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/enum_tuple.rs (line 24)
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
fn main() {
    let bar = short('b')
        .long("bar")
        .help("some bar command")
        .argument::<String>("BAR")
        .optional();

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

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

    println!("{:#?}", opt);
}
More examples
Hide additional examples
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/dynamic-tree.rs (line 67)
61
62
63
64
65
66
67
68
69
70
71
72
73
74
fn make_parser(item: &Cog) -> Box<dyn Parser<&'static str>> {
    match item {
        Cog::Command {
            help,
            name,
            operation,
        } => Box::new(pure(*operation).to_options().descr(*help).command(name)),
        Cog::Group { name, help, nested } => {
            let nested = nested.iter().map(make_parser).collect::<Vec<_>>();
            let inner = choose(nested);
            inner.to_options().descr(*help).command(name).boxed()
        }
    }
}
examples/customize_help.rs (line 11)
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
    let opt = short('d')
        .help("Release the dragon")
        .switch()
        .to_options()
        .descr("I am a program and I do things")
        .header("Sometimes they even work.")
        .footer("Beware `-d`, dragons be here")
        .with_usage(|doc| {
            let mut u = Doc::default();
            u.emphasis("You can call it with following flags:");
            u.text(" ");
            u.doc(&doc);
            u
        })
        .run();

    println!("{:?}", opt);
}
examples/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);
}
source

pub fn header<B: Into<Doc>>(self, header: B) -> Self

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
19
20
21
22
23
24
fn main() {
    let opt = short('d')
        .help("Release the dragon")
        .switch()
        .to_options()
        .descr("I am a program and I do things")
        .header("Sometimes they even work.")
        .footer("Beware `-d`, dragons be here")
        .with_usage(|doc| {
            let mut u = Doc::default();
            u.emphasis("You can call it with following flags:");
            u.text(" ");
            u.doc(&doc);
            u
        })
        .run();

    println!("{:?}", opt);
}
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);
}
source

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

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
19
20
21
22
23
24
fn main() {
    let opt = short('d')
        .help("Release the dragon")
        .switch()
        .to_options()
        .descr("I am a program and I do things")
        .header("Sometimes they even work.")
        .footer("Beware `-d`, dragons be here")
        .with_usage(|doc| {
            let mut u = Doc::default();
            u.emphasis("You can call it with following flags:");
            u.text(" ");
            u.doc(&doc);
            u
        })
        .run();

    println!("{:?}", opt);
}
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 25-33)
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
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.fallback_to_usage().run());
}
source

pub fn usage<B>(self, usage: B) -> Self
where B: Into<Doc>,

Set custom usage field

Custom usage field to use instead of one derived by bpaf.

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

pub fn options() -> OptionParser<Options> {
    let release = short('r')
        .long("release")
        .help("Perform actions in release mode")
        .switch();

    let binary = short('b')
        .long("binary")
        .help("Use this binary")
        .argument("BIN");

    construct!(Options { release, binary })
        .to_options()
        .usage("Usage: my_program [--release] [--binary=BIN] ...")
}

fn main() {
    println!("{:?}", options().run())
}
Derive example
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options, usage("Usage: my_program [--release] [--binary=BIN] ..."))]
pub struct Options {
    #[bpaf(short, long)]
    /// Perform actions in release mode
    release: bool,
    #[bpaf(short, long, argument("BIN"))]
    /// Use this binary
    binary: String,
}

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

Method usage lets you to override the whole usage line

$ app --help

Usage: my_program [--release] [--binary=BIN] ...

Available options:
-r, --release
Perform actions in release mode
-b, --binary=BIN
Use this binary
-h, --help
Prints help information

It doesn’t alter parser’s behavior otherwise

$ app
Error: expected --binary=BIN, pass --help for usage information
$ app -r --binary test
Options { release: true, binary: "test" }
Examples found in repository?
examples/very_custom_usage.rs (line 33)
32
33
34
fn cmd_usage() -> impl Parser<Cmd> {
    cmd().usage("A very custom usage goes here").command("cmd")
}
source

pub fn with_usage<F>(self, f: F) -> Self
where F: Fn(Doc) -> Doc,

Generate new usage line using automatically derived usage

You can customize the surroundings of the usage line while still having part that frequently changes generated by bpaf

#[derive(Debug, Clone)]
pub struct Options {
    release: bool,
    binary: String,
}

pub fn options() -> OptionParser<Options> {
    let release = short('r')
        .long("release")
        .help("Perform actions in release mode")
        .switch();

    let binary = short('b')
        .long("binary")
        .help("Use this binary")
        .argument("BIN");

    construct!(Options { release, binary })
        .to_options()
        .with_usage(|u| {
            let mut doc = Doc::default();
            doc.emphasis("Usage: ");
            doc.literal("my_program");
            doc.text(" ");
            doc.doc(&u);
            doc
        })
}

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

with_usage lets you to place some custom text around generated usage line

$ app --help

Usage: my_program [-r] -b=BIN

Available options:
-r, --release
Perform actions in release mode
-b, --binary=BIN
Use this binary
-h, --help
Prints help information

It doesn’t alter parser’s behavior otherwise

$ app
Error: expected --binary=BIN, pass --help for usage information
$ app -r --binary test
Options { release: true, binary: "test" }

At the moment this method is not directly supported by derive API, but since it gives you an object of OptionParser<T> type you can alter it using Combinatoric API:

#[derive(Debug, Clone, Bpaf)] {
pub struct Options {
    ...
}

fn my_decor(usage: Doc) -> Doc {
    ...
}

fn main() {
    let options = options().with_usage(my_decor).run();
    ...
}
Examples found in repository?
examples/customize_help.rs (lines 14-20)
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
    let opt = short('d')
        .help("Release the dragon")
        .switch()
        .to_options()
        .descr("I am a program and I do things")
        .header("Sometimes they even work.")
        .footer("Beware `-d`, dragons be here")
        .with_usage(|doc| {
            let mut u = Doc::default();
            u.emphasis("You can call it with following flags:");
            u.text(" ");
            u.doc(&doc);
            u
        })
        .run();

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

pub fn check_invariants(&self, _cosmetic: bool)

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

source

pub fn help_parser(self, parser: NamedArg) -> Self

Customize parser for --help

By default bpaf displays help when program is called with either --help or -h, you can customize those names and description in the help message

Note, --help is something user expects to work

#[derive(Debug, Clone)]
pub struct Options {
    package: Option<String>,
}

pub fn options() -> OptionParser<Options> {
    let help = long("help").short('H').help("Renders help information");
    let version = long("version")
        .short('v')
        .help("Renders version information");
    let package = short('p')
        .help("Package to check")
        .argument("SPEC")
        .optional();

    construct!(Options { package })
        .to_options()
        .descr("Command with custom flags for help and version")
        .version("0.42")
        .help_parser(help)
        .version_parser(version)
}

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

This example replaces description and short name for --help parser. Long name works as is

$ app --help

Command with custom flags for help and version

Usage: app [-p=SPEC]

Available options:
-p=SPEC
Package to check
-H, --help
Renders help information
-v, --version
Renders version information

Short name is now capitalized

$ app -H

Command with custom flags for help and version

Usage: app [-p=SPEC]

Available options:
-p=SPEC
Package to check
-H, --help
Renders help information
-v, --version
Renders version information

and old short name no longer works.

$ app -h
Error: -h is not expected in this context

Same with --version parser - new description, original long name and custom short name are both working

$ app --version

Version: 0.42

$ app -v

Version: 0.42

source

pub fn version_parser(self, parser: NamedArg) -> Self

Customize parser for --version

By default bpaf displays version information when program is called with either --version or -V (and version is available), you can customize those names and description in the help message

Note, --version is something user expects to work

#[derive(Debug, Clone)]
pub struct Options {
    package: Option<String>,
}

pub fn options() -> OptionParser<Options> {
    let help = long("help").short('H').help("Renders help information");
    let version = long("version")
        .short('v')
        .help("Renders version information");
    let package = short('p')
        .help("Package to check")
        .argument("SPEC")
        .optional();

    construct!(Options { package })
        .to_options()
        .descr("Command with custom flags for help and version")
        .version("0.42")
        .help_parser(help)
        .version_parser(version)
}

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

This example replaces description and short name for --help parser. Long name works as is

$ app --help

Command with custom flags for help and version

Usage: app [-p=SPEC]

Available options:
-p=SPEC
Package to check
-H, --help
Renders help information
-v, --version
Renders version information

Short name is now capitalized

$ app -H

Command with custom flags for help and version

Usage: app [-p=SPEC]

Available options:
-p=SPEC
Package to check
-H, --help
Renders help information
-v, --version
Renders version information

and old short name no longer works.

$ app -h
Error: -h is not expected in this context

Same with --version parser - new description, original long name and custom short name are both working

$ app --version

Version: 0.42

$ app -v

Version: 0.42

source

pub fn fallback_to_usage(self) -> Self

Print help if app was called with no parameters

By default bpaf tries to parse command line options and displays the best possible error it can come up with. If application requires a subcommand or some argument and user specified none - it might be a better experience for user to print the help message.

// create option parser in a usual way, derive or combinatoric API
let opts = options().fallback_to_usage().run();
Examples found in repository?
examples/derive_commands.rs (line 58)
57
58
59
fn main() {
    println!("{:?}", action().fallback_to_usage().run());
}
More examples
Hide additional examples
examples/derive-smart-pointer.rs (line 30)
29
30
31
32
fn main() {
    let xs = options().fallback_to_usage().run();
    println!("{xs:?}");
}
examples/basic.rs (line 82)
81
82
83
84
fn main() {
    let opts = opts().fallback_to_usage().run();
    println!("{:#?}", opts);
}
examples/csample.rs (line 35)
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
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.fallback_to_usage().run());
}
source

pub fn max_width(self, width: usize) -> Self

Set the width of the help message printed to the terminal upon failure

By default, the help message is printed with a width of 100 characters. This method allows to change where the help message is wrapped.

Setting the max width too low may negatively affect the readability of the help message. Also, the alignment padding of broken lines is always applied.

source§

impl<T> OptionParser<T>

source

pub fn command(self, name: &'static str) -> ParseCommand<T>
where T: 'static,

Parse a subcommand

Subcommands allow to use a totally independent parser inside a current one. Inner parser can have its own help message, description, version and so on. You can nest them arbitrarily too.

§Important restriction

When parsing command arguments from command lines you should have parsers for all your named values before parsers for commands and positional items. In derive API fields parsed as positional should be at the end of your struct/enum. Same rule applies to parsers with positional fields or commands inside: such parsers should go to the end as well.

Use check_invariants in your test to ensure correctness.

For example for non positional non_pos and a command command parsers

let valid = construct!(non_pos(), command());
let invalid = construct!(command(), non_pos());

bpaf panics during help generation unless if this restriction holds

You can attach a single visible short alias and multiple hiddden short and long aliases using short and long methods.

Combinatoric example
#[derive(Debug, Clone)]
pub struct Cmd {
    flag: bool,
    arg: usize,
}

#[derive(Debug, Clone)]
pub struct Options {
    flag: bool,
    cmd: Cmd,
}

fn cmd() -> impl Parser<Cmd> {
    let flag = long("flag")
        .help("This flag is specific to command")
        .switch();
    let arg = long("arg").argument::<usize>("ARG");
    construct!(Cmd { flag, arg })
        .to_options()
        .descr("Command to do something")
        .command("cmd")
        // you can chain add extra short and long names
        .short('c')
}

pub fn options() -> OptionParser<Options> {
    let flag = long("flag")
        .help("This flag is specific to the outer layer")
        .switch();
    construct!(Options { flag, cmd() }).to_options()
}

fn main() {
    println!("{:?}", options().run())
}
Derive example
#[derive(Debug, Clone, Bpaf)]
// `command` annotation with no name gets the name from the object it is attached to,
// but you can override it using something like #[bpaf(command("my_command"))]
// you can chain more short and long names here to serve as aliases
#[bpaf(command("cmd"), short('c'))]
/// Command to do something
pub struct Cmd {
    /// This flag is specific to command
    flag: bool,
    arg: usize,
}

#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    /// This flag is specific to the outer layer
    flag: bool,
    #[bpaf(external)]
    cmd: Cmd,
}

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

Commands show up on both outer level help

$ app --help

Usage: app [--flag] COMMAND ...

Available options:
--flag
This flag is specific to the outer layer
-h, --help
Prints help information

Available commands:
cmd, c
Command to do something

As well as showing their own help

$ app cmd --help

Command to do something

Usage: app cmd [--flag] --arg=ARG

Available options:
--flag
This flag is specific to command
--arg=ARG
-h, --help
Prints help information

In this example there’s only one command and it is required, so is the argument inside of it

$ app cmd --arg 42
Options { flag: false, cmd: Cmd { flag: false, arg: 42 } }

If you don’t specify this command - parsing will fail

You can have the same flag names inside and outside of the command, but it might be confusing for the end user. This example enables the outer flag

$ app --flag cmd --arg 42
Options { flag: true, cmd: Cmd { flag: false, arg: 42 } }

And this one - both inside and outside

$ app --flag cmd --arg 42 --flag
Options { flag: true, cmd: Cmd { flag: true, arg: 42 } }

And that’s the confusing part - unless you add context restrictions with adjacent and parse command first - outer flag wins. So it’s best not to mix names on different levels

$ app cmd --arg 42 --flag
Options { flag: true, cmd: Cmd { flag: false, arg: 42 } }

To represent multiple possible commands it is convenient to use enums

Combinatoric example
#[derive(Debug, Clone)]
pub enum Options {
    /// Run a binary
    Run {
        /// Name of a binary to run
        bin: String,

        /// Arguments to pass to a binary
        args: Vec<String>,
    },
    /// Compile a binary
    Build {
        /// Name of a binary to build
        bin: String,

        /// Compile the binary in release mode
        release: bool,
    },
}

// combine mode gives more flexibility to share the same code across multiple parsers
fn run() -> impl Parser<Options> {
    let bin = long("bin").help("Name of a binary to run").argument("BIN");
    let args = positional("ARG")
        .strict()
        .help("Arguments to pass to a binary")
        .many();

    construct!(Options::Run { bin, args })
}

pub fn options() -> OptionParser<Options> {
    let run = run().to_options().descr("Run a binary").command("run");

    let bin = long("bin")
        .help("Name of a binary to build ")
        .argument("BIN");
    let release = long("release")
        .help("Compile the binary in release mode")
        .switch();
    let build = construct!(Options::Build { bin, release })
        .to_options()
        .descr("Compile a binary")
        .command("build");

    construct!([run, build]).to_options()
}

fn main() {
    println!("{:?}", options().run())
}
Derive example
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub enum Options {
    #[bpaf(command)]
    /// Run a binary
    Run {
        #[bpaf(argument("BIN"))]
        /// Name of a binary to run
        bin: String,

        #[bpaf(positional("ARG"), strict, many)]
        /// Arguments to pass to a binary
        args: Vec<String>,
    },
    #[bpaf(command)]
    /// Compile a binary
    Build {
        #[bpaf(argument("BIN"))]
        /// Name of a binary to build
        bin: String,

        /// Compile the binary in release mode
        release: bool,
    },
}

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

Help contains both commands, bpaf takes short command description from the inner command description

$ app --help

Usage: app COMMAND ...

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

Available commands:
run
Run a binary
build
Compile a binary

Same as before each command gets its own help message

$ app run --help

Run a binary

Usage: app run --bin=BIN -- [ARG]...

Available positional items:
ARG
Arguments to pass to a binary

Available options:
--bin=BIN
Name of a binary to run
-h, --help
Prints help information

And can be executed separately

$ app run --bin basic
Run { bin: "basic", args: [] }
$ app build --bin demo --release
Build { bin: "demo", release: true }
Examples found in repository?
examples/very_custom_usage.rs (line 33)
32
33
34
fn cmd_usage() -> impl Parser<Cmd> {
    cmd().usage("A very custom usage goes here").command("cmd")
}
More examples
Hide additional examples
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])
}
examples/enum_tuple.rs (line 27)
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
fn main() {
    let bar = short('b')
        .long("bar")
        .help("some bar command")
        .argument::<String>("BAR")
        .optional();

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

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

    println!("{:#?}", opt);
}
examples/dynamic-tree.rs (line 67)
61
62
63
64
65
66
67
68
69
70
71
72
73
74
fn make_parser(item: &Cog) -> Box<dyn Parser<&'static str>> {
    match item {
        Cog::Command {
            help,
            name,
            operation,
        } => Box::new(pure(*operation).to_options().descr(*help).command(name)),
        Cog::Group { name, help, nested } => {
            let nested = nested.iter().map(make_parser).collect::<Vec<_>>();
            let inner = choose(nested);
            inner.to_options().descr(*help).command(name).boxed()
        }
    }
}
examples/coreutils.rs (line 269)
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
    pub fn parse_cat() -> impl Parser<Cat> {
        construct!(extra(), cat())
            .map(|(extra, mut cat)| {
                if extra.show_all {
                    cat.show_tabs = true;
                    cat.show_ends = true;
                    cat.show_nonprinting = true;
                }
                if extra.show_non_printing_ends {
                    cat.show_nonprinting = true;
                    cat.show_ends = true;
                }
                if extra.number_nonblank {
                    cat.number = NumberingMode::NonEmpty;
                }
                cat
            })
            .to_options()
            .command("cat")
    }
examples/git.rs (line 38)
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 = fetch.command("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 = add.command("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);
}

Auto Trait Implementations§

§

impl<T> Freeze for OptionParser<T>

§

impl<T> !RefUnwindSafe for OptionParser<T>

§

impl<T> !Send for OptionParser<T>

§

impl<T> !Sync for OptionParser<T>

§

impl<T> Unpin for OptionParser<T>

§

impl<T> !UnwindSafe for OptionParser<T>

Blanket Implementations§

source§

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

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

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

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

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

source§

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

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

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

source§

fn into(self) -> U

Calls U::from(self).

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

source§

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

§

type Error = Infallible

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

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

Performs the conversion.
source§

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

§

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

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

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

Performs the conversion.