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>
impl<T> OptionParser<T>
Sourcepub fn render_html(&self, app: impl Into<String>) -> String
Available on crate feature docgen
only.
pub fn render_html(&self, app: impl Into<String>) -> String
docgen
only.Render command line documentation for the app into html/markdown mix
Sourcepub fn render_markdown(&self, app: impl Into<String>) -> String
Available on crate feature docgen
only.
pub fn render_markdown(&self, app: impl Into<String>) -> String
docgen
only.Render command line documentation for the app into Markdown
Source§impl<T> OptionParser<T>
impl<T> OptionParser<T>
Source§impl<T> OptionParser<T>
impl<T> OptionParser<T>
Sourcepub fn run(self) -> Twhere
Self: Sized,
pub fn run(self) -> Twhere
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();
}
Examples found in repository?
More examples
- examples/derive_show_asm.rs
- examples/env_logger.rs
- examples/find.rs
- examples/numeric_prefix.rs
- examples/xorg.rs
- examples/many_comma_separated_args.rs
- examples/many_comma_separated_args_derive.rs
- examples/derive_commands.rs
- examples/fallback_command.rs
- examples/positional_derive.rs
- examples/coreutils.rs
- examples/derive-smart-pointer.rs
- examples/basic.rs
- examples/compression.rs
- examples/shared_args.rs
- examples/dynamic-tree.rs
- examples/enum_in_args.rs
- examples/at_least_two.rs
- examples/env_variable.rs
- examples/filenames.rs
- examples/ex_positional.rs
- examples/top_to_bottom.rs
- examples/flatten.rs
- examples/cargo-cmd.rs
- examples/enum_tuple.rs
- examples/negative.rs
- examples/customize_help.rs
- examples/no_import.rs
- examples/multiple_fallback.rs
- examples/sensors.rs
- examples/cat.rs
- examples/rectangle.rs
- examples/git.rs
- examples/csample.rs
- examples/confusing.rs
- examples/verbose.rs
- examples/travel.rs
Sourcepub fn try_run(self) -> Result<T, ParseFailure>where
Self: Sized,
👎Deprecated: You should switch to equivalent parser.run_inner(Args::current_args())
pub fn try_run(self) -> Result<T, ParseFailure>where
Self: Sized,
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.
Sourcepub fn run_inner<'a>(
&self,
args: impl Into<Args<'a>>,
) -> Result<T, ParseFailure>where
Self: Sized,
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.
Sourcepub fn version<B: Into<Doc>>(self, version: B) -> Self
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
Sourcepub fn descr<B: Into<Doc>>(self, descr: B) -> Self
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?
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
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);
}
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()
}
}
}
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);
}
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());
}
}
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);
}
Sourcepub fn header<B: Into<Doc>>(self, header: B) -> Self
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?
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
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);
}
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?
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
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);
}
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());
}
Sourcepub fn usage<B>(self, usage: B) -> Self
pub fn usage<B>(self, usage: B) -> Self
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
Usage: my_program [--release] [--binary=BIN] ...
- -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
Error: expected --binary=BIN, pass --help for usage information
Options { release: true, binary: "test" }
Sourcepub fn with_usage<F>(self, f: F) -> Self
pub fn with_usage<F>(self, f: F) -> Self
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
Usage: my_program [-r] -b=BIN
- -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
Error: expected --binary=BIN, pass --help for usage information
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?
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);
}
Sourcepub fn check_invariants(&self, _cosmetic: bool)
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
Sourcepub fn help_parser(self, parser: NamedArg) -> Self
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
Command with custom flags for help and version
Usage: app [-p=SPEC]
- -p=SPEC
- Package to check
- -H, --help
- Renders help information
- -v, --version
- Renders version information
Short name is now capitalized
Command with custom flags for help and version
Usage: app [-p=SPEC]
- -p=SPEC
- Package to check
- -H, --help
- Renders help information
- -v, --version
- Renders version information
and old short name no longer works.
Error: -h is not expected in this context
Same with --version
parser - new description, original long name and custom short name are
both working
Version: 0.42
Version: 0.42
Sourcepub fn version_parser(self, parser: NamedArg) -> Self
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
Command with custom flags for help and version
Usage: app [-p=SPEC]
- -p=SPEC
- Package to check
- -H, --help
- Renders help information
- -v, --version
- Renders version information
Short name is now capitalized
Command with custom flags for help and version
Usage: app [-p=SPEC]
- -p=SPEC
- Package to check
- -H, --help
- Renders help information
- -v, --version
- Renders version information
and old short name no longer works.
Error: -h is not expected in this context
Same with --version
parser - new description, original long name and custom short name are
both working
Version: 0.42
Version: 0.42
Sourcepub fn fallback_to_usage(self) -> Self
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?
More examples
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());
}
Sourcepub fn max_width(self, width: usize) -> Self
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>
impl<T> OptionParser<T>
Sourcepub fn command(self, name: &'static str) -> ParseCommand<T>where
T: 'static,
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
Usage: app [--flag] COMMAND ...
- --flag
- This flag is specific to the outer layer
- -h, --help
- Prints help information
- cmd, c
- Command to do something
As well as showing their own help
Command to do something
Usage: app cmd [--flag] --arg=ARG
- --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
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
Options { flag: true, cmd: Cmd { flag: false, arg: 42 } }
And this one - both inside and outside
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
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
Usage: app COMMAND ...
- -h, --help
- Prints help information
- run
- Run a binary
- build
- Compile a binary
Same as before each command gets its own help message
Run a binary
Usage: app run --bin=BIN -- [ARG]...
- ARG
- Arguments to pass to a binary
- --bin=BIN
- Name of a binary to run
- -h, --help
- Prints help information
And can be executed separately
Run { bin: "basic", args: [] }
Build { bin: "demo", release: true }
Examples found in repository?
More examples
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);
}
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()
}
}
}
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")
}
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);
}