Struct 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)
15fn main() {
16    let bar = short('b')
17        .long("bar")
18        .help("some bar command")
19        .argument::<String>("BAR")
20        .optional();
21
22    let bar_cmd = construct!(Foo { bar })
23        .to_options()
24        .descr("This command will try to do foo given a bar argument");
25
26    let opt = bar_cmd
27        .command("foo")
28        .help("command for doing foo")
29        .map(Command::Foo)
30        .to_options()
31        .run();
32
33    println!("{:#?}", opt);
34}
More examples
Hide additional examples
examples/negative.rs (line 15)
5fn main() {
6    let age = long("age").argument::<i64>("AGE");
7    let msg = "\
8To pass a value that starts with a dash requres one one of two special syntaxes:
9
10This will pass '-1' to '--age' handler and leave remaining arguments as is
11    --age=-1
12This will transform everything after '--' into non flags, '--age' will handle '-1'
13and positional handlers will be able to handle the rest.
14    --age -- -1";
15    let num = age.to_options().descr(msg).run();
16    println!("age: {}", num);
17}
examples/dynamic-tree.rs (line 67)
61fn make_parser(item: &Cog) -> Box<dyn Parser<&'static str>> {
62    match item {
63        Cog::Command {
64            help,
65            name,
66            operation,
67        } => Box::new(pure(*operation).to_options().descr(*help).command(name)),
68        Cog::Group { name, help, nested } => {
69            let nested = nested.iter().map(make_parser).collect::<Vec<_>>();
70            let inner = choose(nested);
71            inner.to_options().descr(*help).command(name).boxed()
72        }
73    }
74}
examples/customize_help.rs (line 11)
6fn main() {
7    let opt = short('d')
8        .help("Release the dragon")
9        .switch()
10        .to_options()
11        .descr("I am a program and I do things")
12        .header("Sometimes they even work.")
13        .footer("Beware `-d`, dragons be here")
14        .with_usage(|doc| {
15            let mut u = Doc::default();
16            u.emphasis("You can call it with following flags:");
17            u.text(" ");
18            u.doc(&doc);
19            u
20        })
21        .run();
22
23    println!("{:?}", opt);
24}
examples/cat.rs (line 31)
15fn main() {
16    let file = positional::<OsString>("FILE")
17        .help("File name to concatenate, with no FILE or when FILE is -, read standard input")
18        .optional()
19        .parse::<_, Box<dyn Read>, std::io::Error>(|path| {
20            Ok(if let Some(path) = path {
21                if path == "-" {
22                    Box::new(stdin())
23                } else {
24                    Box::new(File::open(path)?)
25                }
26            } else {
27                Box::new(stdin())
28            })
29        })
30        .to_options()
31        .descr("Concatenate a file to standard output")
32        .run();
33
34    let reader = BufReader::new(file);
35
36    for line in reader.lines() {
37        println!("{}", line.unwrap());
38    }
39}
examples/rectangle.rs (line 41)
19fn main() {
20    let width = short('w')
21        .long("width")
22        .help("Width of the rectangle")
23        .argument::<usize>("PX");
24
25    let height = short('h')
26        .long("height")
27        .help("Height of the rectangle")
28        .argument::<usize>("PX");
29
30    let rect = construct!(Rect { width, height })
31        .group_help("Rectangle is defined by width and height in meters")
32        .optional();
33
34    let verbose = short('v')
35        .long("verbose")
36        .help("Print computation steps")
37        .switch();
38
39    let opt = construct!(Out { verbose, rect })
40        .to_options()
41        .descr("This program calculates rectangle's area")
42        .header("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
43        .footer("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^")
44        .run();
45    println!("{:#?}", opt);
46}
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)
6fn main() {
7    let opt = short('d')
8        .help("Release the dragon")
9        .switch()
10        .to_options()
11        .descr("I am a program and I do things")
12        .header("Sometimes they even work.")
13        .footer("Beware `-d`, dragons be here")
14        .with_usage(|doc| {
15            let mut u = Doc::default();
16            u.emphasis("You can call it with following flags:");
17            u.text(" ");
18            u.doc(&doc);
19            u
20        })
21        .run();
22
23    println!("{:?}", opt);
24}
More examples
Hide additional examples
examples/rectangle.rs (line 42)
19fn main() {
20    let width = short('w')
21        .long("width")
22        .help("Width of the rectangle")
23        .argument::<usize>("PX");
24
25    let height = short('h')
26        .long("height")
27        .help("Height of the rectangle")
28        .argument::<usize>("PX");
29
30    let rect = construct!(Rect { width, height })
31        .group_help("Rectangle is defined by width and height in meters")
32        .optional();
33
34    let verbose = short('v')
35        .long("verbose")
36        .help("Print computation steps")
37        .switch();
38
39    let opt = construct!(Out { verbose, rect })
40        .to_options()
41        .descr("This program calculates rectangle's area")
42        .header("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
43        .footer("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^")
44        .run();
45    println!("{:#?}", opt);
46}
examples/travel.rs (line 68)
16fn main() {
17    // Suppose we want to calcualte a total time of a travel, where parts of
18    // a travel can be given either as pairs of speed and distance or just by time.
19    // Speed can be given by KPH or MPH. Distance - either miles or km.
20
21    // parsers for speeds. Both speeds are converted to the same units
22    let mph = long("mph")
23        .help("speed in MPH")
24        .argument::<f64>("SPEED")
25        .map(|x| x * 1.6);
26    let kph = long("kph").help("Speed in KPH").argument::<f64>("SPEED");
27
28    // speed is either kph or mph, conversion to mph is handled by the parser
29    let speed = construct!([mph, kph]);
30
31    // parsers for distances, both are converted to the same units
32    let km = long("km").help("Distance in KM").argument::<f64>("KMs");
33    let mi = long("mi")
34        .help("distance in miles")
35        .argument::<f64>("MILES")
36        .map(|x| x * 1.6);
37    let dist = construct!([mi, km]);
38
39    // time, presumably in seconds
40    let time = long("time")
41        .help("Travel time in hours")
42        .argument::<f64>("TIME");
43
44    // parsed time is trivially converted to time segment
45    let segment_time = time.map(Segment::Time);
46
47    // parsed speed/distance is packed into SpeedDistance segment
48    let segment_speed = construct!(Segment::SpeedDistance { speed, dist });
49
50    // segment can be either of two defined
51    let segment = construct!([segment_speed, segment_time]);
52
53    // and we have several of them.
54    let parser = segment
55        .many()
56        .guard(|x| !x.is_empty(), "need at least one segment")
57        .guard(
58            |x| x.len() < 10,
59            "for more than 9 segments you need to purchase a premium subscription",
60        );
61
62    let descr = "Accepts one or more travel segments";
63    let header = "You need to specify one or more travel segments, segment is defined by
64a pair of speed and distance or by time.
65
66This example defines two separate travel segments, one given by speed/distance combo and one by time
67    travel --km 180 --kph 35 --time";
68    let decorated = parser.to_options().descr(descr).header(header);
69
70    // help message tries to explain what's needed:
71    // either --time OR one speed and one distance, both can be given in miles or km.
72    // number of speed flags must correspond to number of distance flags, more or
73    // less results in parser error messages
74    let opt = decorated.run();
75
76    println!("{:#?}", opt);
77}
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)
6fn main() {
7    let opt = short('d')
8        .help("Release the dragon")
9        .switch()
10        .to_options()
11        .descr("I am a program and I do things")
12        .header("Sometimes they even work.")
13        .footer("Beware `-d`, dragons be here")
14        .with_usage(|doc| {
15            let mut u = Doc::default();
16            u.emphasis("You can call it with following flags:");
17            u.text(" ");
18            u.doc(&doc);
19            u
20        })
21        .run();
22
23    println!("{:?}", opt);
24}
More examples
Hide additional examples
examples/rectangle.rs (line 43)
19fn main() {
20    let width = short('w')
21        .long("width")
22        .help("Width of the rectangle")
23        .argument::<usize>("PX");
24
25    let height = short('h')
26        .long("height")
27        .help("Height of the rectangle")
28        .argument::<usize>("PX");
29
30    let rect = construct!(Rect { width, height })
31        .group_help("Rectangle is defined by width and height in meters")
32        .optional();
33
34    let verbose = short('v')
35        .long("verbose")
36        .help("Print computation steps")
37        .switch();
38
39    let opt = construct!(Out { verbose, rect })
40        .to_options()
41        .descr("This program calculates rectangle's area")
42        .header("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
43        .footer("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^")
44        .run();
45    println!("{:#?}", opt);
46}
examples/csample.rs (lines 25-33)
12fn main() {
13    use bpaf::*;
14
15    let a = short('a').long("avocado").help("Use avocado").switch();
16    let b = short('b').long("banana").help("Use banana").switch();
17    let bb = long("bananananana").help("I'm Batman").switch();
18    let c = long("calculator")
19        .help("calculator expression")
20        .argument::<String>("EXPR")
21        .complete(complete_calculator);
22    let parser = construct!(a, b, bb, c)
23        .to_options()
24        .descr("Dynamic autocomplete example")
25        .footer(
26            "\
27    bpaf supports dynamic autocompletion for a few shells, make sure your binary is in $PATH
28     and try using one of those this output should go into a file that depends on your shell:
29    $ csample --bpaf-complete-style-bash
30    $ csample --bpaf-complete-style-zsh
31    $ csample --bpaf-complete-style-fish
32    $ csample --bpaf-complete-style-elvish",
33        );
34
35    println!("{:?}", parser.fallback_to_usage().run());
36}
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)
32fn cmd_usage() -> impl Parser<Cmd> {
33    cmd().usage("A very custom usage goes here").command("cmd")
34}
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)
6fn main() {
7    let opt = short('d')
8        .help("Release the dragon")
9        .switch()
10        .to_options()
11        .descr("I am a program and I do things")
12        .header("Sometimes they even work.")
13        .footer("Beware `-d`, dragons be here")
14        .with_usage(|doc| {
15            let mut u = Doc::default();
16            u.emphasis("You can call it with following flags:");
17            u.text(" ");
18            u.doc(&doc);
19            u
20        })
21        .run();
22
23    println!("{:?}", opt);
24}
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();

For derive macro you can specify fallback_to_usage in top level annotations for options and for individual commands if fallback to useage is the desired behavior:

#[derive(Debug, Clone, Bpaf)]
enum Commands {
    #[bpaf(command, fallback_to_usage)]
    Action {
        ...
    }
}

Or

#[derive(Debug, Clone, Bpaf)]
#[bpaf(options, fallback_to_usage)]
struct Options {
    ...
}

fn main() {
    let options = options().run(); // falls back to usage
}
Examples found in repository?
examples/derive_commands.rs (line 58)
57fn main() {
58    println!("{:?}", action().fallback_to_usage().run());
59}
More examples
Hide additional examples
examples/derive-smart-pointer.rs (line 30)
29fn main() {
30    let xs = options().fallback_to_usage().run();
31    println!("{xs:?}");
32}
examples/basic.rs (line 82)
81fn main() {
82    let opts = opts().fallback_to_usage().run();
83    println!("{:#?}", opts);
84}
examples/csample.rs (line 35)
12fn main() {
13    use bpaf::*;
14
15    let a = short('a').long("avocado").help("Use avocado").switch();
16    let b = short('b').long("banana").help("Use banana").switch();
17    let bb = long("bananananana").help("I'm Batman").switch();
18    let c = long("calculator")
19        .help("calculator expression")
20        .argument::<String>("EXPR")
21        .complete(complete_calculator);
22    let parser = construct!(a, b, bb, c)
23        .to_options()
24        .descr("Dynamic autocomplete example")
25        .footer(
26            "\
27    bpaf supports dynamic autocompletion for a few shells, make sure your binary is in $PATH
28     and try using one of those this output should go into a file that depends on your shell:
29    $ csample --bpaf-complete-style-bash
30    $ csample --bpaf-complete-style-zsh
31    $ csample --bpaf-complete-style-fish
32    $ csample --bpaf-complete-style-elvish",
33        );
34
35    println!("{:?}", parser.fallback_to_usage().run());
36}
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)
32fn cmd_usage() -> impl Parser<Cmd> {
33    cmd().usage("A very custom usage goes here").command("cmd")
34}
More examples
Hide additional examples
examples/shared_args.rs (line 30)
28fn parse_command() -> impl Parser<(Command, Vec<String>)> {
29    let action = action().map(Command::Action);
30    let action = construct!(action, shared()).to_options().command("action");
31    let build = build().map(Command::Build);
32    let build = construct!(build, shared()).to_options().command("build");
33    construct!([action, build])
34}
examples/enum_tuple.rs (line 27)
15fn main() {
16    let bar = short('b')
17        .long("bar")
18        .help("some bar command")
19        .argument::<String>("BAR")
20        .optional();
21
22    let bar_cmd = construct!(Foo { bar })
23        .to_options()
24        .descr("This command will try to do foo given a bar argument");
25
26    let opt = bar_cmd
27        .command("foo")
28        .help("command for doing foo")
29        .map(Command::Foo)
30        .to_options()
31        .run();
32
33    println!("{:#?}", opt);
34}
examples/dynamic-tree.rs (line 67)
61fn make_parser(item: &Cog) -> Box<dyn Parser<&'static str>> {
62    match item {
63        Cog::Command {
64            help,
65            name,
66            operation,
67        } => Box::new(pure(*operation).to_options().descr(*help).command(name)),
68        Cog::Group { name, help, nested } => {
69            let nested = nested.iter().map(make_parser).collect::<Vec<_>>();
70            let inner = choose(nested);
71            inner.to_options().descr(*help).command(name).boxed()
72        }
73    }
74}
examples/coreutils.rs (line 269)
251    pub fn parse_cat() -> impl Parser<Cat> {
252        construct!(extra(), cat())
253            .map(|(extra, mut cat)| {
254                if extra.show_all {
255                    cat.show_tabs = true;
256                    cat.show_ends = true;
257                    cat.show_nonprinting = true;
258                }
259                if extra.show_non_printing_ends {
260                    cat.show_nonprinting = true;
261                    cat.show_ends = true;
262                }
263                if extra.number_nonblank {
264                    cat.number = NumberingMode::NonEmpty;
265                }
266                cat
267            })
268            .to_options()
269            .command("cat")
270    }
examples/git.rs (line 38)
26fn main() {
27    let dry_run = long("dry_run").switch();
28    let all = long("all").switch();
29    let repository = positional::<String>("SRC").fallback("origin".to_string());
30    let fetch = construct!(Opt::Fetch {
31        dry_run,
32        all,
33        repository
34    })
35    .to_options()
36    .descr("fetches branches from remote repository");
37
38    let fetch_cmd = fetch.command("fetch");
39
40    let interactive = short('i').switch();
41    let all = long("all").switch();
42    let files = positional::<PathBuf>("FILE").many();
43    let add = construct!(Opt::Add {
44        interactive,
45        all,
46        files
47    })
48    .to_options()
49    .descr("add files to the staging area");
50
51    let add_cmd = add.command("add").help("add files to the staging area");
52
53    let opt = construct!([fetch_cmd, add_cmd])
54        .to_options()
55        .descr("The stupid content tracker")
56        .run();
57
58    println!("{:?}", opt);
59}

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>,

Source§

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>,

Source§

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.