pub trait Parser<T> {
Show 19 methods
// Provided methods
fn many(self) -> ParseMany<Self>
where Self: Sized { ... }
fn collect<C>(self) -> ParseCollect<Self, C, T>
where C: FromIterator<T>,
Self: Sized { ... }
fn some(self, message: &'static str) -> ParseSome<Self>
where Self: Sized + Parser<T> { ... }
fn optional(self) -> ParseOptional<Self>
where Self: Sized + Parser<T> { ... }
fn count(self) -> ParseCount<Self, T>
where Self: Sized + Parser<T> { ... }
fn last(self) -> ParseLast<Self>
where Self: Sized + Parser<T> { ... }
fn parse<F, R, E>(self, f: F) -> ParseWith<T, Self, F, E, R>
where Self: Sized + Parser<T>,
F: Fn(T) -> Result<R, E>,
E: ToString { ... }
fn map<F, R>(self, map: F) -> ParseMap<T, Self, F, R>
where Self: Sized + Parser<T>,
F: Fn(T) -> R + 'static { ... }
fn guard<F>(self, check: F, message: &'static str) -> ParseGuard<Self, F>
where Self: Sized + Parser<T>,
F: Fn(&T) -> bool { ... }
fn fallback(self, value: T) -> ParseFallback<Self, T>
where Self: Sized + Parser<T> { ... }
fn fallback_with<F, E>(
self,
fallback: F,
) -> ParseFallbackWith<T, Self, F, E>
where Self: Sized + Parser<T>,
F: Fn() -> Result<T, E>,
E: ToString { ... }
fn hide(self) -> ParseHide<Self>
where Self: Sized + Parser<T> { ... }
fn hide_usage(self) -> ParseUsage<Self>
where Self: Sized + Parser<T> { ... }
fn custom_usage<M>(self, usage: M) -> ParseUsage<Self>
where M: Into<Doc>,
Self: Sized + Parser<T> { ... }
fn group_help<M: Into<Doc>>(self, message: M) -> ParseGroupHelp<Self>
where Self: Sized + Parser<T> { ... }
fn with_group_help<F>(self, f: F) -> ParseWithGroupHelp<Self, F>
where Self: Sized + Parser<T>,
F: Fn(MetaInfo<'_>) -> Doc { ... }
fn to_options(self) -> OptionParser<T>
where Self: Sized + Parser<T> + 'static { ... }
fn run(self) -> T
where Self: Sized + Parser<T> + 'static { ... }
fn boxed(self) -> Box<dyn Parser<T>>
where Self: Sized + Parser<T> + 'static { ... }
}Expand description
Simple or composed argument parser
§Overview
It’s best to think of an object implementing Parser trait as a container with a value
inside that is composable with other Parser containers using construct! and the only
way to extract this value is by transforming it to OptionParser with
to_options and running it with run. At which
point you either get your value out or bpaf would generate a message describing a problem
(missing argument, validation failure, user requested help, etc) and the program would
exit.
Values inside can be of any type for as long as they implement Debug, Clone and
there are no lifetimes other than static.
When consuming the values you can jump straight to a value that implements
FromStr trait and then transform it into something that your program would use. Alternatively,
you can consume either String or OsString and parse that by hand. It’s better to perform
as much parsing and validation inside the Parser as possible so the program itself gets
strictly typed and correct value while the user gets immediate feedback on what’s wrong with the
arguments they pass.
Order of operations matters, each subsequent parser gets the output of the earlier one. Both
parsers a and b would consume multiple numeric values, each less than 10, but a
validates a single value and then consumes multiple of them already validated, while b first
consumes and then performs validation. The former approach is usually more readable.
let a = short('a').argument::<usize>("N")
.guard(|&a| a < 10, "`a` must be below 10")
.many();
let b = short('b').argument::<usize>("N")
.many()
.guard(|bs| bs.iter().all(|&b| b < 10), "`b` must be below 10");The same logic applies to derive API - the current type depends on the order of annotations:
#[derive(Bpaf, Debug, Clone)]
struct Simple {
#[bpaf(argument("N"), guard(less_than_10, "`a` must be below 10"), many)]
a: Vec<usize>,
#[bpaf(argument("N"), many, guard(all_less_than_10, "`b` must be below 10"))]
b: Vec<usize>,
}For example suppose your program needs the user to specify dimensions of a rectangle, with sides being 1..20 units long and the total area must not exceed 200 units square. A parser that consumes it might look like this:
#[derive(Debug, Copy, Clone)]
struct Rectangle {
width: u32,
height: u32,
}
fn rectangle() -> impl Parser<Rectangle> {
let invalid_size = "Sides of a rectangle must be 1..20 units long";
let invalid_area = "Area of a rectangle must not exceed 200 units square";
let width = long("width")
.help("Width of the rectangle")
.argument::<u32>("PX")
.guard(|&x| 1 <= x && x <= 10, invalid_size);
let height = long("height")
.help("Height of the rectangle")
.argument::<u32>("PX")
.guard(|&x| 1 <= x && x <= 10, invalid_size);
construct!(Rectangle { width, height })
.guard(|&r| r.width * r.height <= 400, invalid_area)
}§Derive specific considerations
Every method defined on this trait belongs to the postprocessing section of the field
annotation. bpaf would try to figure out what chain to use for as long as there are no
options changing the type: you can use fallback,
fallback_with, guard, hide and
group_help but not the rest of them.
#[derive(Debug, Clone, Bpaf)]
struct Options {
// no annotation at all - `bpaf` inserts implicit `argument` and gets the right type
number_1: u32,
// fallback isn't changing the type so `bpaf` still handles it
#[bpaf(fallback(42))]
number_2: u32,
// `bpaf` inserts implicit `argument`, `optional` and the right type
number_3: Option<u32>,
// fails to compile: you need to specify `argument`
// #[bpaf(optional)]
// number_4: Option<u32>,
#[bpaf(argument("N"), optional)]
number_5: Option<u32>,
// explicit consumer and a full postprocessing chain
#[bpaf(argument::<u32>("N"), optional)]
number_6: Option<u32>,
}Provided Methods§
Sourcefn many(self) -> ParseMany<Self>where
Self: Sized,
fn many(self) -> ParseMany<Self>where
Self: Sized,
Consume zero or more items from a command line and collect them into a Vec
many preserves any parsing failures and propagates them outwards, with an extra
catch statement you can instead stop at the first value
that failed to parse and ignore it and all the subsequent ones.
many will collect at most one result that does not consume anything from the argument
list allowing using it in combination with any parsers with a fallback. After the first
one, it will keep collecting the results as long as they consume something.
For derive usage bpaf would insert implicit many when the resulting type is a
vector.
Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
argument: Vec<u32>,
switches: Vec<bool>,
}
pub fn options() -> OptionParser<Options> {
let argument = long("argument")
.help("important argument")
.argument("ARG")
.many();
let switches = long("switch").help("some switch").switch().many();
construct!(Options { argument, switches }).to_options()
}
fn main() {
println!("{:?}", options().run())
}Derive example
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
/// important argument
argument: Vec<u32>,
/// some switch
#[bpaf(long("switch"), switch)]
switches: Vec<bool>,
}
fn main() {
println!("{:?}", options().run())
}Output
In usage lines many items are indicated with ...
Usage: app [--argument=ARG]... [--switch]...
- --argument=ARG
- important argument
- --switch
- some switch
- -h, --help
- Prints help information
Run inner parser as many times as possible collecting all the new results
First false is collected from a switch even if it is not consuming anything
Options { argument: [10, 20], switches: [false] }
If there’s no matching parameters - it would produce an empty vector. Note, in case of
switch parser or other parsers that can succeed without consuming anything
it would capture that value so many captures the first one of those.
You can use req_flag to avoid that.
Options { argument: [], switches: [false] }
For parsers that can succeed without consuming anything such as flag or switch - many
only collects values as long as they produce something
Options { argument: [], switches: [true, true] }
§See also
some also collects results to a vector but requires at least one
element to succeed, collect collects results into a FromIterator
structure
Sourcefn collect<C>(self) -> ParseCollect<Self, C, T>where
C: FromIterator<T>,
Self: Sized,
fn collect<C>(self) -> ParseCollect<Self, C, T>where
C: FromIterator<T>,
Self: Sized,
Transform parser into a collection parser
A generic variant of many, instead of collecting into a vector
it collects into any collection that implements FromIterator trait
collect preserves any parsing failures and propagates them outwards, with extra
catch statement you can instead stop at the first value
that failed to parse and ignore it and all the subsequent ones.
Combinatoric example
use std::collections::BTreeSet;
#[derive(Debug, Clone)]
pub struct Options {
argument: BTreeSet<u32>,
switches: BTreeSet<bool>,
}
pub fn options() -> OptionParser<Options> {
let argument = long("argument")
.help("important argument")
.argument("ARG")
.collect();
let switches = long("switch").help("some switch").switch().collect();
construct!(Options { argument, switches }).to_options()
}
fn main() {
println!("{:?}", options().run())
}Derive example
use std::collections::BTreeSet;
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
/// important argument
#[bpaf(argument::<u32>("ARG"), collect)]
argument: BTreeSet<u32>,
/// some switch
#[bpaf(long("switch"), switch, collect)]
switches: BTreeSet<bool>,
}
fn main() {
println!("{:?}", options().run())
}Output
In usage lines collect items are indicated with ...
Usage: app --argument=ARG... [--switch]...
- --argument=ARG
- important argument
- --switch
- some switch
- -h, --help
- Prints help information
Run inner parser as many times as possible collecting all the new results
First false is collected from a switch even if it is not consuming anything
Options { argument: {10, 20}, switches: {false} }
If there’s no matching parameters - it would produce an empty set. Note, in case of
switch parser or other parsers that can succeed without consuming anything
it would capture that value so many captures the first one of those.
You can use req_flag to avoid that.
Options { argument: {}, switches: {false} }
For parsers that can succeed without consuming anything such as flag or switch - many
only collects values as long as they produce something
Options { argument: {}, switches: {true} }
collect will collect at most one result that does not consume anything from the argument
list allowing using it in combination of any parsers with a fallback. After the first one
it will keep collecting the results as long as they consume something.
Sourcefn some(self, message: &'static str) -> ParseSome<Self>
fn some(self, message: &'static str) -> ParseSome<Self>
Consume one or more items from a command line and collect them into a Vec
Takes a string used as an error message if there are no specified parameters
some preserves any parsing failures and propagates them outwards, with an extra
catch statement you can instead stop at the first value
that failed to parse and ignore it and all the subsequent ones.
some will collect at most one result that does not consume anything from the argument
list allowing using it in combination with any parsers with a fallback. After the first
one, it will keep collecting the results as long as they consume something.
Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
argument: Vec<u32>,
switches: Vec<bool>,
}
pub fn options() -> OptionParser<Options> {
let argument = long("argument")
.help("important argument")
.argument("ARG")
.some("want at least one argument");
let switches = long("switch")
.help("some switch")
.req_flag(true)
.some("want at least one switch");
construct!(Options { argument, switches }).to_options()
}
fn main() {
println!("{:?}", options().run())
}Derive example
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
/// important argument
#[bpaf(argument("ARG"), some("want at least one argument"))]
argument: Vec<u32>,
/// some switch
#[bpaf(long("switch"), req_flag(true), some("want at least one switch"))]
switches: Vec<bool>,
}
fn main() {
println!("{:?}", options().run())
}Output
In usage lines some items are indicated with ...
Usage: app --argument=ARG... --switch...
- --argument=ARG
- important argument
- --switch
- some switch
- -h, --help
- Prints help information
Run inner parser as many times as possible collecting all the new results, but unlike
many needs to collect at least one element to succeed
Options { argument: [10, 20], switches: [true] }
With not enough parameters to satisfy both parsers at least once - it fails
Error: want at least one argument
both parsers need to succeed to create a struct
Error: want at least one switch
For parsers that can succeed without consuming anything such as flag or switch - some
only collects values as long as they produce something
Options { argument: [10], switches: [true] }
§See also
many also collects results to a vector but succeeds with
no matching values. collect collects results into a FromIterator
structure
Sourcefn optional(self) -> ParseOptional<Self>
fn optional(self) -> ParseOptional<Self>
Turn a required argument into an optional one
optional converts any missing items into None and passes the remaining parsing
failures untouched. With an extra catch statement, you can handle
those failures too.
§Derive usage
By default, bpaf would automatically use optional for fields of type Option<T>,
for as long as it’s not prevented from doing so by present postprocessing options.
But it’s also possible to specify it explicitly.
Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
version: Option<usize>,
feature: Option<String>,
}
pub fn options() -> OptionParser<Options> {
let version = long("version").argument("VERS").optional();
let feature = long("feature").argument("FEAT").optional();
construct!(Options { version, feature }).to_options()
}
fn main() {
println!("{:?}", options().run())
}Derive example
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
#[bpaf(argument("VERS"))]
version: Option<usize>,
#[bpaf(argument("FEAT"))]
feature: Option<String>,
}
fn main() {
println!("{:?}", options().run())
}Output
bpaf encases optional arguments in usage with []
Usage: app [--version=VERS] [--feature=FEAT]
- --version=VERS
- --feature=FEAT
- -h, --help
- Prints help information
Missing arguments are turned into None
Options { version: None, feature: None }
Present values are Some
Options { version: Some(10), feature: None }
As usual you can specify both
Options { version: Some(10), feature: Some("feat") }
Sourcefn count(self) -> ParseCount<Self, T>
fn count(self) -> ParseCount<Self, T>
Count how many times the inner parser succeeds, and return that number.
When you are dealing with a parser that can succeed without consuming
anything from a command line - bpaf will count first such success as well.
Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
verbosity: usize,
}
pub fn options() -> OptionParser<Options> {
let verbosity = short('v')
.long("verbose")
.help("Increase the verbosity level")
.req_flag(())
.count();
construct!(Options { verbosity }).to_options()
}
fn main() {
println!("{:?}", options().run())
}Derive example
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
/// Increase the verbosity level
#[bpaf(short('v'), long("verbose"), req_flag(()), count)]
verbosity: usize,
}
fn main() {
println!("{:?}", options().run())
}Output
In --help message req_flag look similarly to switch and
flag
Usage: app [-v]...
- -v, --verbose
- Increase the verbosity level
- -h, --help
- Prints help information
Since parser uses req_flag it succeeds exactly 0 times if there’s no parameters
Options { verbosity: 0 }
If it was specified - count tracks it a discards parsed values
Options { verbosity: 3 }
Options { verbosity: 2 }
Sourcefn last(self) -> ParseLast<Self>
fn last(self) -> ParseLast<Self>
Apply the inner parser as many times as it succeeds, return the last value
You can use this to allow users to pick contradicting options
Combinatoric example
#[derive(Debug, Clone)]
pub enum Style {
Intel,
Att,
Llvm,
}
#[derive(Debug, Clone)]
pub enum Report {
/// Include defailed report
Detailed,
/// Include minimal report
Minimal,
/// No preferences
Undecided,
}
#[derive(Debug, Clone)]
pub struct Options {
style: Style,
report: Report,
}
pub fn options() -> OptionParser<Options> {
let intel = long("intel")
.help("Show assembly using Intel style")
.req_flag(Style::Intel);
let att = long("att")
.help("Show assembly using AT&T style")
.req_flag(Style::Att);
let llvm = long("llvm").help("Show llvm-ir").req_flag(Style::Llvm);
let style = construct!([intel, att, llvm]).last();
let detailed = long("detailed")
.help("Include detailed report")
.req_flag(Report::Detailed);
let minimal = long("minimal")
.help("Include minimal report")
.req_flag(Report::Minimal);
let report = construct!([detailed, minimal])
.last()
.fallback(Report::Undecided);
construct!(Options { style, report }).to_options()
}
fn main() {
println!("{:?}", options().run())
}Derive example
#[derive(Debug, Clone, Bpaf)]
#[bpaf(last)]
pub enum Style {
/// Show assembly using Intel style
Intel,
/// Show assembly using AT&T style
Att,
/// Show llvm-ir
Llvm,
}
#[derive(Debug, Clone, Bpaf)]
#[bpaf(last, fallback(Report::Undecided))]
pub enum Report {
/// Include detailed report
Detailed,
/// Include minimal report
Minimal,
#[bpaf(skip)]
/// No preferences
Undecided,
}
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
// external here uses explicit reference to function `style`
// generated above
#[bpaf(external(style))]
style: Style,
// here reference is implicit and derived from field name: `report`
#[bpaf(external)]
report: Report,
}
fn main() {
println!("{:?}", options().run())
}Output
In --help message last shows that inner parser can run multiple times
Usage: app (--intel | --att | --llvm)... [--detailed | --minimal]...
- --intel
- Show assembly using Intel style
- --att
- Show assembly using AT&T style
- --llvm
- Show llvm-ir
- --detailed
- Include detailed report
- --minimal
- Include minimal report
- -h, --help
- Prints help information
style takes one of several possible values and last lets user to pass it several times
Options { style: Intel, report: Undecided }
Options { style: Att, report: Undecided }
Options { style: Intel, report: Undecided }
same goes with report
Options { style: Intel, report: Detailed }
Options { style: Att, report: Minimal }
Sourcefn parse<F, R, E>(self, f: F) -> ParseWith<T, Self, F, E, R>
fn parse<F, R, E>(self, f: F) -> ParseWith<T, Self, F, E, R>
Apply a failing transformation to a contained value
Transformation preserves the present/absent state of the value: to parse an optional value you
can either first try to parse it and then mark it as optional or first
deal with the optionality and then parse a value wrapped in Option. In most cases
the former approach is more concise.
Similarly, it is possible to parse multiple items with many or
some by either parsing a single item first and then turning it into a Vec
or collecting them into a Vec first and then parsing the whole vector. The former approach
is more concise.
This is a most general of transforming parsers and you can express
map and guard in terms of it.
Examples are a bit artificial, to parse a value from a string you can specify
the type directly in the argument’s turbofish and then apply map.
§Derive usage:
parse takes a single parameter: function name to call. Function type should match
parameter F used by parse in combinatoric API.
Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
number: u32,
}
pub fn options() -> OptionParser<Options> {
let number = long("number")
.argument::<String>("N")
// normally you'd use argument::<u32> to get a numeric
// value and `map` to double it
.parse::<_, _, ParseIntError>(|s| Ok(u32::from_str(&s)? * 2));
construct!(Options { number }).to_options()
}
fn main() {
println!("{:?}", options().run())
}Derive example
fn twice_the_num(s: String) -> Result<u32, ParseIntError> {
Ok(u32::from_str(&s)? * 2)
}
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
#[bpaf(argument::<String>("N"), parse(twice_the_num))]
number: u32,
}
fn main() {
println!("{:?}", options().run())
}Output
parse don’t make any changes to generated --help message
Usage: app --number=N
- --number=N
- -h, --help
- Prints help information
You can use parse to apply arbitrary failing transformation to any input.
For example here --number takes a numerical value and doubles it
Options { number: 20 }
But if function inside the parser fails - user will get the error back unless it’s handled in some other way
Error: couldn't parse ten: invalid digit found in string
Sourcefn map<F, R>(self, map: F) -> ParseMap<T, Self, F, R>
fn map<F, R>(self, map: F) -> ParseMap<T, Self, F, R>
Apply a pure transformation to a contained value
A common case of the parse method, exists mostly for convenience.
§Derive usage:
The map takes a single parameter: function name to call. This function should transform
the value produced by the parser into a new value of the same or different type.
Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
number: u32,
}
pub fn options() -> OptionParser<Options> {
let number = long("number").argument::<u32>("N").map(|x| x * 2);
construct!(Options { number }).to_options()
}
fn main() {
println!("{:?}", options().run())
}Derive example
fn twice_the_num(n: u32) -> u32 {
n * 2
}
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
#[bpaf(argument::<u32>("N"), map(twice_the_num))]
number: u32,
}
fn main() {
println!("{:?}", options().run())
}Output
map don’t make any changes to generated --help message
You can use map to apply arbitrary pure transformation to any input.
Here --number takes a numerical value and doubles it
Options { number: 20 }
But if function inside the parser fails - user will get the error back unless it’s handled
in some way. In fact here execution never reaches map function -
argument tries to parse ten as a number, fails and reports the error
Error: couldn't parse ten: invalid digit found in string
Sourcefn guard<F>(self, check: F, message: &'static str) -> ParseGuard<Self, F>
fn guard<F>(self, check: F, message: &'static str) -> ParseGuard<Self, F>
Validate or fail with a message
If the value doesn’t satisfy the constraint - the parser fails with the specified error message.
§Derive usage
Derive variant of the guard takes a function name instead of a closure, mostly to keep things
clean. The second argument can be either a string literal or a constant name for a static str.
Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
number: u32,
}
pub fn options() -> OptionParser<Options> {
let number = long("number").argument::<u32>("N").guard(
|n| *n <= 10,
"Values greater than 10 are only available in the DLC pack!",
);
construct!(Options { number }).to_options()
}
fn main() {
println!("{:?}", options().run())
}Derive example
fn dlc_check(number: &u32) -> bool {
*number <= 10
}
const DLC_NEEDED: &str = "Values greater than 10 are only available in the DLC pack!";
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
#[bpaf(argument("N"), guard(dlc_check, DLC_NEEDED))]
number: u32,
}
fn main() {
println!("{:?}", options().run())
}Output
guard don’t make any changes to generated --help message
Usage: app --number=N
- --number=N
- -h, --help
- Prints help information
You can use guard to set boundary limits or perform other checks on parsed values. Parser accepts numbers below 10
Options { number: 5 }
And fails with the error message on higher values:
Error: 11: Values greater than 10 are only available in the DLC pack!
But if function inside the parser fails - user will get the error back unless it’s handled in some way
Error: couldn't parse ten: invalid digit found in string
Sourcefn fallback(self, value: T) -> ParseFallback<Self, T>
fn fallback(self, value: T) -> ParseFallback<Self, T>
Use this value as default if the value isn’t present on a command line
Parser would still fail if the value is present but failure comes from some transformation
Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
jobs: usize,
}
pub fn options() -> OptionParser<Options> {
let jobs = long("jobs")
.help("Number of jobs")
.argument("JOBS")
.fallback(42)
.display_fallback();
construct!(Options { jobs }).to_options()
}
fn main() {
println!("{:?}", options().run())
}Derive example
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
#[allow(dead_code)]
pub struct Options {
/// Number of jobs
#[bpaf(argument("JOBS"), fallback(42), display_fallback)]
jobs: usize,
}
fn main() {
println!("{:?}", options().run())
}Output
fallback changes parser to fallback to a default value used when argument is not specified
Options { jobs: 42 }
If value is present - fallback value is ignored
Options { jobs: 10 }
Parsing errors are preserved and presented to the user
Error: couldn't parse ten: invalid digit found in string
With display_fallback,
debug_fallback, and
format_fallback, you can make it so the default value
is visible in the --help output.
Usage: app [--jobs=JOBS]
- --jobs=JOBS
- Number of jobs
- [default: 42]
- -h, --help
- Prints help information
§See also
fallback_with would allow to try to fallback to a value that
comes from a failing computation such as reading a file. By default, the fallback value will
not be shown in the --help output; you can change that by using
display_fallback,
debug_fallback, or
format_fallback.
Sourcefn fallback_with<F, E>(self, fallback: F) -> ParseFallbackWith<T, Self, F, E>
fn fallback_with<F, E>(self, fallback: F) -> ParseFallbackWith<T, Self, F, E>
Use value produced by this function as default if the value isn’t present
Would still fail if the value is present but failure comes from some earlier transformation
Combinatoric example
fn try_to_get_version() -> Result<usize, &'static str> {
Ok(42)
}
#[derive(Debug, Clone)]
pub struct Options {
version: usize,
}
pub fn options() -> OptionParser<Options> {
let version = long("version")
.help("Specify protocol version")
.argument("VERS")
.fallback_with(try_to_get_version)
.display_fallback();
construct!(Options { version }).to_options()
}
fn main() {
println!("{:?}", options().run())
}Derive example
fn try_to_get_version() -> Result<usize, &'static str> {
Ok(42)
}
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
#[bpaf(argument("VERS"), fallback_with(try_to_get_version), display_fallback)]
/// Specify protocol version
version: usize,
}
fn main() {
println!("{:?}", options().run())
}Output
fallback_with changes parser to fallback to a value that comes from a potentially failing
computation when argument is not specified
Options { version: 42 }
If value is present - fallback value is ignored
Options { version: 10 }
Parsing errors are preserved and presented to the user
Error: couldn't parse ten: invalid digit found in string
bpaf encases parsers with fallback value of some sort in usage with []
Usage: app [--version=VERS]
- --version=VERS
- Specify protocol version
- [default: 42]
- -h, --help
- Prints help information
§See also
fallback implements similar logic expect that failures aren’t expected.
By default, the fallback value will
not be shown in the --help output; you can change that by using
display_fallback,
debug_fallback, or
format_fallback.
Sourcefn hide(self) -> ParseHide<Self>
fn hide(self) -> ParseHide<Self>
Ignore this parser during any sort of help generation
Best used for optional parsers or parsers with a defined fallback, usually for implementing backward compatibility or hidden aliases
Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
argument: u32,
switch: bool,
}
pub fn options() -> OptionParser<Options> {
let argument = long("argument")
.help("important argument")
.argument("ARG")
.fallback(30);
let switch = long("switch").help("secret switch").switch().hide();
construct!(Options { argument, switch }).to_options()
}
fn main() {
println!("{:?}", options().run())
}Derive example
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
/// important argument
#[bpaf(fallback(30))]
argument: u32,
/// secret switch
#[bpaf(hide)]
switch: bool,
}
fn main() {
println!("{:?}", options().run())
}Output
hide removes the inner parser from any help or autocompletion logic
Usage: app [--argument=ARG]
- --argument=ARG
- important argument
- -h, --help
- Prints help information
But doesn’t change the parsing behavior in any way otherwise
Options { argument: 32, switch: false }
Options { argument: 42, switch: true }
Sourcefn hide_usage(self) -> ParseUsage<Self>
fn hide_usage(self) -> ParseUsage<Self>
Ignore this parser when generating a usage line
Parsers hidden from usage will still show up in the available arguments list. Best used on
optional things that augment the main application functionality but not define it.
Alternatively, you can use custom_usage to replace a single
option or a group of them with some other text.
Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
argument: u32,
switch: bool,
}
pub fn options() -> OptionParser<Options> {
let argument = long("argument")
.help("important argument")
.argument("ARG")
.fallback(30);
let switch = long("switch")
.help("not that important switch")
.switch()
.hide_usage();
construct!(Options { argument, switch }).to_options()
}
fn main() {
println!("{:?}", options().run())
}Derive example
#[allow(dead_code)]
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
/// important argument
#[bpaf(fallback(30))]
argument: u32,
/// not that important switch
#[bpaf(hide_usage)]
switch: bool,
}
fn main() {
println!("{:?}", options().run())
}Output
hide_usage hides the inner parser from the generated usage line, but not from the rest of the help or completion
Usage: app [--argument=ARG]
- --argument=ARG
- important argument
- --switch
- not that important switch
- -h, --help
- Prints help information
But doesn’t change the parsing behavior in any way otherwise
Options { argument: 32, switch: false }
Options { argument: 32, switch: true }
Sourcefn custom_usage<M>(self, usage: M) -> ParseUsage<Self>
fn custom_usage<M>(self, usage: M) -> ParseUsage<Self>
Customize how this parser looks like in the usage line
Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
binary: Option<String>,
package: Option<String>,
}
pub fn options() -> OptionParser<Options> {
let binary = short('b')
.long("binary")
.help("Binary to run")
.argument("BIN")
.optional()
.custom_usage(&[
("--binary", Style::Literal),
("=", Style::Text),
("BINARY", Style::Metavar),
]);
let package = short('p')
.long("package")
.help("Package to check")
.argument("PACKAGE")
.optional();
construct!(Options { binary, package }).to_options()
}
fn main() {
println!("{:?}", options().run())
}Derive example
const BINARY_USAGE: &[(&str, Style)] = &[
("--binary", Style::Literal),
("=", Style::Text),
("BINARY", Style::Metavar),
];
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
/// Binary to run
#[bpaf(short, long, argument("BIN"), custom_usage(BINARY_USAGE))]
binary: Option<String>,
/// Package to check
#[bpaf(short, long, argument("PACKAGE"))]
package: Option<String>,
}
fn main() {
println!("{:?}", options().run())
}Output
custom_usage changes how parser shows up in the “Usage” section of generated --help, note
lack of [], long name instead of a short one and different metavariable value
Usage: app --binary=BINARY [-p=PACKAGE]
- -b, --binary=BIN
- Binary to run
- -p, --package=PACKAGE
- Package to check
- -h, --help
- Prints help information
Parsing behavior stays unchanged
Options { binary: Some("cargo-asm"), package: Some("cargo-show-asm") }
Sourcefn group_help<M: Into<Doc>>(self, message: M) -> ParseGroupHelp<Self>
fn group_help<M: Into<Doc>>(self, message: M) -> ParseGroupHelp<Self>
Attach a help message to a complex parser
bpaf inserts the group help message before the block with all the fields
from the inner parser and an empty line after the block.
Combinatoric example
#[derive(Debug, Clone)]
pub struct Rectangle {
width: u32,
height: u32,
}
#[derive(Debug, Clone)]
pub struct Options {
argument: u32,
rectangle: Rectangle,
}
pub fn options() -> OptionParser<Options> {
let argument = long("argument")
.help("important argument")
.argument("ARG")
.fallback(30);
let width = long("width")
.help("Width of the rectangle")
.argument("W")
.fallback(10);
let height = long("height")
.help("Height of the rectangle")
.argument("H")
.fallback(10);
let rectangle = construct!(Rectangle { width, height }).group_help("Takes a rectangle");
construct!(Options {
argument,
rectangle
})
.to_options()
}
fn main() {
println!("{:?}", options().run())
}Derive example
#[derive(Debug, Clone, Bpaf)]
pub struct Rectangle {
/// Width of the rectangle
#[bpaf(argument("W"), fallback(10))]
width: u32,
/// Height of the rectangle
#[bpaf(argument("H"), fallback(10))]
height: u32,
}
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
/// important argument
#[bpaf(fallback(30))]
argument: u32,
/// secret switch
#[bpaf(external, group_help("Takes a rectangle"))]
rectangle: Rectangle,
}
fn main() {
println!("{:?}", options().run())
}Output
group_help adds extra decoration for the inner group in --help message
Usage: app [--argument=ARG] [--width=W] [--height=H]
- --width=W
- Width of the rectangle
- --height=H
- Height of the rectangle
- --argument=ARG
- important argument
- -h, --help
- Prints help information
And doesn’t change the parsing behavior in any way
Options { argument: 32, rectangle: Rectangle { width: 20, height: 13 } }
Sourcefn with_group_help<F>(self, f: F) -> ParseWithGroupHelp<Self, F>
fn with_group_help<F>(self, f: F) -> ParseWithGroupHelp<Self, F>
Make a help message for a complex parser from its MetaInfo
use bpaf::doc::*;
use bpaf::*;
#[derive(Debug, Clone)]
pub struct Rectangle {
width: u32,
height: u32,
}
#[derive(Debug, Clone)]
pub struct Options {
argument: u32,
rectangle: Rectangle,
}
fn generate_rectangle_help(meta: MetaInfo) -> Doc {
let mut buf = Doc::default();
buf.text("The app takes a rectangle defined by width and height\n\nYou can customize the screen size using ");
buf.meta(meta, true);
buf.text(" parameters");
buf
}
pub fn options() -> OptionParser<Options> {
let argument = long("argument")
.help("important argument")
.argument("ARG")
.fallback(30);
let width = long("width")
.help("Width of the rectangle")
.argument("W")
.fallback(10);
let height = long("height")
.help("Height of the rectangle")
.argument("H")
.fallback(10);
let rectangle =
construct!(Rectangle { width, height }).with_group_help(generate_rectangle_help);
construct!(Options {
argument,
rectangle
})
.to_options()
}
fn main() {
println!("{:?}", options().run())
}Output
with_group_help lets you write longer description for group of options that can also refer to
those options. Similar to group_help encased optios are separated from
the rest by a blank line.
Invoking help with a single --help flag renders shot(er) version of the help message
that contanis only the first paragraph for each block:
Usage: app [--argument=ARG] [--width=W] [--height=H]
- --width=W
- Width of the rectangle
- --height=H
- Height of the rectangle
- --argument=ARG
- important argument
- -h, --help
- Prints help information
Invoking help with double --help --help flag renders the full help message with all the
descriptions added
Usage: app [--argument=ARG] [--width=W] [--height=H]
- --width=W
- Width of the rectangle
- --height=H
- Height of the rectangle
- --argument=ARG
- important argument
- -h, --help
- Prints help information
Other than rendering the help message that there’s no interactions with other parsers
Options { argument: 30, rectangle: Rectangle { width: 120, height: 11 } }
Options { argument: 12, rectangle: Rectangle { width: 10, height: 10 } }
Sourcefn to_options(self) -> OptionParser<T>
fn to_options(self) -> OptionParser<T>
Transform Parser into OptionParser to get ready to run it
§Derive usage
Add a top-level options annotation to generate OptionParser instead of default
Parser.
In addition to options annotation, you can also specify either version or
version(value) annotation. The former uses version from cargo, later uses the
specified value which should be an expression of type &'static str, see
version.
Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
argument: u32,
}
pub fn options() -> OptionParser<Options> {
let argument = short('i').argument::<u32>("ARG");
construct!(Options { argument })
.to_options()
.version("3.1415")
.descr("This is a short description")
.header("It can contain multiple blocks, this block goes before options")
.footer("This one goes after")
}
fn main() {
println!("{:?}", options().run())
}Derive example
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options, version("3.1415"))]
/// This is a short description
///
///
/// It can contain multiple blocks, this block goes before options
///
///
/// This one goes after
pub struct Options {
#[bpaf(short('i'))]
argument: u32,
}
fn main() {
println!("{:?}", options().run())
}Output
In addition to all the arguments specified by user bpaf adds a few more. One of them is
--help:
This is a short description
Usage: app -i=ARG
It can contain multiple blocks, this block goes before options
- -i=ARG
- -h, --help
- Prints help information
- -V, --version
- Prints version information
This one goes after
The other one is --version - passing a string literal or something like
env!("CARGO_PKG_VERSION") to get version from cargo directly usually works
Version: 3.1415
Other than that bpaf tries its best to provide a helpful error messages
Error: expected -i=ARG, pass --help for usage information
And if all parsers are satisfied run produces the result
Options { argument: 10 }
§See also
There’s some methods implemented on OptionParser directly to customize the appearance
Sourcefn run(self) -> T
fn run(self) -> T
Finalize and run the parser
Generally, you’d want to use Parser::to_options to finalize the parser and OptionParser::run,
but this also works for simple cases:
fn main() {
let name = short('n').long("name").argument::<String>("USER").run();
// do things with name
}Sourcefn boxed(self) -> Box<dyn Parser<T>>
fn boxed(self) -> Box<dyn Parser<T>>
Create a boxed representation for a parser
The boxed parser doesn’t expose internal representation in its type and allows to return of different parsers in different conditional branches
You can create it with a single argument construct macro or by using boxed annotation
pub fn options() -> OptionParser<f64> {
let miles = long("distance")
.help("distance in miles")
.argument::<f64>("MILES")
.map(|d| d * 1.609344);
let km = long("distance")
.help("distance in km")
.argument::<f64>("KM");
// suppose this is reading from config fule
let use_metric = true;
// without use of `boxed` here branches have different types so it won't typecheck
// boxed make it so branches have the same type as long as they return the same type
let distance = if use_metric {
km.boxed()
} else {
miles.boxed()
};
distance.to_options()
}
fn main() {
println!("{:?}", options().run())
}Output
It is also possible to make dynamic choice about the parsers. This example defines two parsers for distance - imperial and metric and picks one from some source available at runtime only.
Help message will contain only one parser
Usage: app --distance=KM
- --distance=KM
- distance in km
- -h, --help
- Prints help information
and only one parser will produce a result
10.0