pub trait Parser<T> {
Show 15 methods
fn many(self) -> ParseMany<Self>
where
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 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 group_help(self, message: &'static str) -> ParseGroupHelp<Self>
where
Self: Sized + Parser<T>,
{ ... }
fn complete<M, F>(self, op: F) -> ParseComp<Self, F>
where
M: Into<String>,
F: Fn(&T) -> Vec<(M, Option<M>)>,
Self: Sized + Parser<T>,
{ ... }
fn complete_style(self, style: CompleteDecor) -> ParseCompStyle<Self>
where
Self: Sized + Parser<T>,
{ ... }
fn adjacent(self) -> ParseAdjacent<Self>
where
Self: Sized + Parser<T>,
{ ... }
fn anywhere(self) -> ParseAnywhere<Self>
where
Self: Sized + Parser<T>,
{ ... }
fn to_options(self) -> OptionParser<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 are 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’s no lifetimes other than static.
When consuming the values you can jump straight to a value that implements either FromOsStr
or
FromStr
trait then transform into something that your program would actually 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 user gets immediate feedback on what’s wrong with the
arguments they pass.
For example suppose your program needs user to specify a 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_derive
would try to figure out what chain to use for as long as there’s 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_derive` inserts implicit `argument` and gets the right type
number_1: u32,
// fallback isn't changing the type so `bpaf_derive` still handles it
#[bpaf(fallback(42))]
number_2: u32,
// `bpaf_derive` inserts implicit `argument`, `optional` and the right type
number_3: Option<u32>,
// fails to compile: you need to specify a consumer, `argument` or `argument_os`
// #[bpaf(optional)]
// number_4: Option<u32>
// fails to compile: you also need to specify how to go from String to 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
Consume zero or more items from a command line and collect them into Vec
many
preserves any parsing falures 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.
many
only collects elements that only consume something from the argument list.
Combinatoric usage:
fn numbers() -> impl Parser<Vec<u32>> {
short('n')
.argument::<u32>("NUM")
.many()
}
Derive usage:
bpaf
would insert implicit many
when resulting type is a vector
#[derive(Debug, Clone, Bpaf)]
struct Options {
#[bpaf(short, argument("NUM"))]
numbers: Vec<u32>
}
But it’s also possible to specify it explicitly, both cases renerate the same code.
Note, since using many
resets the postprocessing chain - you also need to specify
argument
’s turbofish.
#[derive(Debug, Clone, Bpaf)]
struct Options {
#[bpaf(short, argument::<u32>("NUM"), many)]
numbers: Vec<u32>
}
Example
$ app -n 1 -n 2 -n 3
// [1, 2, 3]
See also
some
also collects results to a vector but requires at least one
element to succeed
Examples found in repository?
More examples
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 verbose() -> impl Parser<usize> {
short('v')
.long("verbose")
.help("Increase the verbosity\nYou can specify it up to 3 times\neither as -v -v -v or as -vvv")
.req_flag(())
.many()
.map(|xs| xs.len())
.guard(|&x| x <= 3, "It doesn't get any more verbose than this")
}
// an argument, parsed and with default value
fn speed() -> impl Parser<f64> {
short('s')
.long("speed")
.help("Set speed")
.argument::<f64>("SPEED")
.fallback(42.0)
}
fn output() -> impl Parser<PathBuf> {
short('o')
.long("output")
.help("output file")
.argument::<PathBuf>("OUTPUT")
}
// no magical name transmogrifications.
fn nb_cars() -> impl Parser<u32> {
short('n').long("nb-cars").argument::<u32>("N")
}
fn files_to_process() -> impl Parser<Vec<PathBuf>> {
short('f')
.long("file")
.help("File to process")
.argument::<PathBuf>("FILE")
.many()
}
28 29 30 31 32 33 34 35 36 37 38 39
fn verbose() -> impl Parser<LevelFilter> {
short('v')
.help("Verbosity level, use multiple times for more verbosity")
.req_flag(())
.many()
.map(|v| {
use LevelFilter::*;
*[Off, Error, Warn, Info, Debug, Trace]
.get(v.len())
.unwrap_or(&Trace)
})
}
Consume one or more items from a command line
Takes a string used as an error message if there’s no specified parameters
some
preserves any parsing falures 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.
some
only collects elements that only consume something from the argument list.
Combinatoric usage:
let numbers
= short('n')
.argument::<u32>("NUM")
.some("Need at least one number");
Derive usage
Since using some
resets the postprocessing chain - you also might need to specify
the type in turbofish:
#[derive(Debug, Clone, Bpaf)]
struct Options {
#[bpaf(short, argument::<u32>("NUM"), some("Need at least one number"))]
numbers: Vec<u32>
}
Example
$ app
// fails with "Need at least one number"
$ app -n 1 -n 2 -n 3
// [1, 2, 3]
See also
many
also collects results to a vector but succeeds with
no matching values
sourcefn optional(self) -> ParseOptional<Self>where
Self: Sized + Parser<T>,
fn optional(self) -> ParseOptional<Self>where
Self: Sized + Parser<T>,
Turn a required argument into optional one
optional
converts any missing items into is None
and passes the remaining parsing
failures untouched. With extra catch
statement you can handle
those failures too.
Combinatoric usage
fn number() -> impl Parser<Option<u32>> {
short('n')
.argument::<u32>("NUM")
.optional()
}
Derive usage
By default bpaf_derive
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
#[derive(Debug, Clone, Bpaf)]
struct Options {
#[bpaf(short, argument("NUM"))]
number: Option<u32>
}
But it’s also possible to specify it explicitly example.
#[derive(Debug, Clone, Bpaf)]
struct Options {
#[bpaf(short, argument::<u32>("NUM"), optional)]
number: Option<u32>
}
Example
$ app
// None
$ app -n 42
// Some(42)
Examples found in repository?
More examples
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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
fn user() -> impl Parser<Option<String>> {
let tag = any::<String>("TAG")
.guard(|s| s == "-user", "not user")
.hide();
let value = positional::<String>("USER");
construct!(tag, value)
.anywhere()
.map(|pair| pair.1)
.optional()
.catch()
}
// parsers -exec xxx yyy zzz ;
fn exec() -> impl Parser<Option<Vec<OsString>>> {
let tag = any::<String>("-exec")
.help("-exec /path/to/command flags and options ;")
.guard(|s| s == "-exec", "not find");
let item = any::<OsString>("ITEM")
.guard(|s| s != ";", "not word")
.many()
.catch()
.hide();
let endtag = any::<String>("END").guard(|s| s == ";", "not eot").hide();
construct!(tag, item, endtag)
.anywhere()
.map(|triple| triple.1)
.optional()
.catch()
}
/// parses symbolic permissions `-perm -mode`, `-perm /mode` and `-perm mode`
fn perm() -> impl Parser<Option<Perm>> {
fn parse_mode(input: &str) -> Result<Perms, String> {
let mut perms = Perms::default();
for c in input.chars() {
match c {
'r' => perms.read = true,
'w' => perms.write = true,
'x' => perms.exec = true,
_ => return Err(format!("{} is not a valid permission string", input)),
}
}
Ok(perms)
}
let tag = any::<String>("-mode").help("-mode (perm | -perm | /perm)");
let mode = any::<String>("mode")
.parse::<_, _, String>(|s| {
if let Some(m) = s.strip_prefix('-') {
Ok(Perm::All(parse_mode(m)?))
} else if let Some(m) = s.strip_prefix('/') {
Ok(Perm::Any(parse_mode(m)?))
} else {
Ok(Perm::Exact(parse_mode(&s)?))
}
})
.hide();
construct!(tag, mode)
.anywhere()
.map(|pair| pair.1)
.optional()
.catch()
}
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
fn main() {
let bar = short('b')
.long("bar")
.help("some bar command")
.argument::<String>("BAR")
.optional();
let bar_cmd = construct!(Foo { bar })
.to_options()
.descr("This command will try to do foo given a bar argument");
let opt = command("foo", bar_cmd)
.help("command for doing foo")
.map(Command::Foo)
.to_options()
.run();
println!("{:#?}", opt);
}
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
fn main() {
// defining a parser in a usual way
let width = short('w').argument::<usize>("WIDTH").fallback(10);
let height = short('h').argument::<usize>("HEIGHT").fallback(10);
let parser = construct!(Opts { width, height });
let cmd = positional::<String>("")
.guard(|s| s == "cmd", "")
.optional()
.hide();
let combined_parser = construct!(cmd, parser).map(|x| x.1);
let opts = combined_parser.to_options().run();
println!("{:?}", opts);
}
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());
}
}
Apply a failing transformation to a contained value
Transformation preserves present/absent state of the value: to parse an optional value you
can either first try to parse
it and then mark as optional
or first
deal with the optionality and then parse a value wrapped in Option
. In most cases
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. 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 given here are a bit artificail, to parse a value from string you can specify
the type directly in argument
’s turbofish
Combinatoric usage:
fn number() -> impl Parser<u32> {
short('n')
.argument::<String>("NUM")
.parse(|s| u32::from_str(&s))
}
Derive usage:
parse
takes a single parameter: function name to call. Function type should match
parameter F
used by parse
in combinatoric API.
fn read_number(s: String) -> Result<u32, ParseIntError> {
u32::from_str(&s)
}
#[derive(Debug, Clone, Bpaf)]
struct Options {
#[bpaf(short, argument::<String>("NUM"), parse(read_number))]
number: u32
}
Example
$ app -n 12
// 12
// fails with "Couldn't parse "pi": invalid numeric literal"
Examples found in repository?
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
fn parse_manifest_path() -> impl Parser<PathBuf> {
long("manifest-path")
.help("Path to Cargo.toml")
.argument::<PathBuf>("PATH")
.parse(|p| {
if p.is_absolute() {
Ok(p)
} else {
std::env::current_dir()
.map(|d| d.join(p))
.and_then(|full_path| full_path.canonicalize())
}
})
.fallback_with(|| std::env::current_dir().map(|x| x.join("Cargo.toml")))
}
More examples
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
fn tag<T>(name: &'static str, meta: &'static str, help: &'static str) -> impl Parser<T>
where
T: FromStr,
<T as std::str::FromStr>::Err: std::fmt::Display,
{
// it is possible to parse OsString here and strip the prefix with os_str_bytes or a similar
// crate
any::<String>(meta)
.help(help)
.parse::<_, _, String>(move |s| match s.strip_prefix(name) {
None => Err("Wrong tag".to_string()),
Some(body) => T::from_str(body).map_err(|e| e.to_string()),
})
.anywhere()
}
11 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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
fn toggle_options(name: &'static str, help: &'static str) -> impl Parser<bool> {
any::<String>(name)
.help(help)
.parse(move |s| {
let (state, cur_name) = if let Some(rest) = s.strip_prefix('+') {
(true, rest)
} else if let Some(rest) = s.strip_prefix('-') {
(false, rest)
} else {
return Err(format!("{} is not a toggle option", s));
};
if cur_name != name {
Err(format!("{} is not a known toggle option name", cur_name))
} else {
Ok(state)
}
})
.anywhere()
}
fn extension() -> impl Parser<(String, bool)> {
let on = any::<String>("+ext")
.help("enable ext <EXT>")
.parse::<_, _, String>(|s| {
if s == "+ext" {
Ok(true)
} else {
Err(String::new())
}
});
let off = any::<String>("-ext")
.help("disable ext <EXT>")
.parse::<_, _, String>(|s| {
if s == "-ext" {
Ok(false)
} else {
Err(String::new())
}
});
let state = construct!([on, off]);
let name = positional::<String>("EXT").hide();
construct!(state, name).map(|(a, b)| (b, a)).anywhere()
}
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());
}
}
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
fn perm() -> impl Parser<Option<Perm>> {
fn parse_mode(input: &str) -> Result<Perms, String> {
let mut perms = Perms::default();
for c in input.chars() {
match c {
'r' => perms.read = true,
'w' => perms.write = true,
'x' => perms.exec = true,
_ => return Err(format!("{} is not a valid permission string", input)),
}
}
Ok(perms)
}
let tag = any::<String>("-mode").help("-mode (perm | -perm | /perm)");
let mode = any::<String>("mode")
.parse::<_, _, String>(|s| {
if let Some(m) = s.strip_prefix('-') {
Ok(Perm::All(parse_mode(m)?))
} else if let Some(m) = s.strip_prefix('/') {
Ok(Perm::Any(parse_mode(m)?))
} else {
Ok(Perm::Exact(parse_mode(&s)?))
}
})
.hide();
construct!(tag, mode)
.anywhere()
.map(|pair| pair.1)
.optional()
.catch()
}
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
fn main() {
let token = long("token")
.help("Token used for complex commands")
.argument::<String>("TOKEN")
.optional();
// start with defining 3 commands: simple, complex1 and complex2
let simple_parser = pure(PreCommand::Simple).to_options();
let simple = command("simple", simple_parser);
let complex1_parser = positional::<i32>("ARG");
let complex1 = command(
"complex1",
construct!(PreCommand::Complex1(complex1_parser))
.to_options()
.descr("This is complex command 1"),
);
let complex2_parser = positional::<i16>("ARG");
let complex2 = command(
"complex1",
construct!(PreCommand::Complex2(complex2_parser))
.to_options()
.descr("This is complex command 2"),
);
// compose then to accept any of those
let preparser = construct!([simple, complex1, complex2]);
// make a parser that accepts optional token and one of incomplete commands
// then create complete command or fail
let parser = construct!(token, preparser).parse(|(token, cmd)| match cmd {
PreCommand::Simple => Ok(Command::Simple),
PreCommand::Complex1(a) => match token {
Some(token) => Ok(Command::Complex1(token, a)),
None => Err("You must specify token to use with --token"),
},
PreCommand::Complex2(a) => match token {
Some(token) => Ok(Command::Complex2(token, a)),
None => Err("You must specify token to use with --token"),
},
});
let cmd = parser.to_options().run();
println!("{:?}", cmd);
}
Apply a pure transformation to a contained value
A common case of parse
method, exists mostly for convenience.
Combinatoric usage
fn number() -> impl Parser<u32> {
short('n')
.argument::<u32>("NUM")
.map(|v| v * 2)
}
Derive usage
fn double(num: u32) -> u32 {
num * 2
}
#[derive(Debug, Clone, Bpaf)]
struct Options {
#[bpaf(short, argument::<u32>("NUM"), map(double))]
number: u32,
}
Example
$ app -n 21
// 42
Examples found in repository?
More examples
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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
fn user() -> impl Parser<Option<String>> {
let tag = any::<String>("TAG")
.guard(|s| s == "-user", "not user")
.hide();
let value = positional::<String>("USER");
construct!(tag, value)
.anywhere()
.map(|pair| pair.1)
.optional()
.catch()
}
// parsers -exec xxx yyy zzz ;
fn exec() -> impl Parser<Option<Vec<OsString>>> {
let tag = any::<String>("-exec")
.help("-exec /path/to/command flags and options ;")
.guard(|s| s == "-exec", "not find");
let item = any::<OsString>("ITEM")
.guard(|s| s != ";", "not word")
.many()
.catch()
.hide();
let endtag = any::<String>("END").guard(|s| s == ";", "not eot").hide();
construct!(tag, item, endtag)
.anywhere()
.map(|triple| triple.1)
.optional()
.catch()
}
/// parses symbolic permissions `-perm -mode`, `-perm /mode` and `-perm mode`
fn perm() -> impl Parser<Option<Perm>> {
fn parse_mode(input: &str) -> Result<Perms, String> {
let mut perms = Perms::default();
for c in input.chars() {
match c {
'r' => perms.read = true,
'w' => perms.write = true,
'x' => perms.exec = true,
_ => return Err(format!("{} is not a valid permission string", input)),
}
}
Ok(perms)
}
let tag = any::<String>("-mode").help("-mode (perm | -perm | /perm)");
let mode = any::<String>("mode")
.parse::<_, _, String>(|s| {
if let Some(m) = s.strip_prefix('-') {
Ok(Perm::All(parse_mode(m)?))
} else if let Some(m) = s.strip_prefix('/') {
Ok(Perm::Any(parse_mode(m)?))
} else {
Ok(Perm::Exact(parse_mode(&s)?))
}
})
.hide();
construct!(tag, mode)
.anywhere()
.map(|pair| pair.1)
.optional()
.catch()
}
28 29 30 31 32 33 34 35 36 37 38 39
fn verbose() -> impl Parser<LevelFilter> {
short('v')
.help("Verbosity level, use multiple times for more verbosity")
.req_flag(())
.many()
.map(|v| {
use LevelFilter::*;
*[Off, Error, Warn, Info, Debug, Trace]
.get(v.len())
.unwrap_or(&Trace)
})
}
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
fn main() {
let bar = short('b')
.long("bar")
.help("some bar command")
.argument::<String>("BAR")
.optional();
let bar_cmd = construct!(Foo { bar })
.to_options()
.descr("This command will try to do foo given a bar argument");
let opt = command("foo", bar_cmd)
.help("command for doing foo")
.map(Command::Foo)
.to_options()
.run();
println!("{:#?}", opt);
}
Validate or fail with a message
If value doesn’t satisfy the constraint - parser fails with the specified error message.
Combinatoric usage
fn number() -> impl Parser<u32> {
short('n')
.argument::<u32>("NUM")
.guard(|n| *n <= 10, "Values greater than 10 are only available in the DLC pack!")
}
Derive usage
Unlike combinator counterpart, derive variant of guard
takes a function name instead
of a closure, mostly to keep things clean. Second argument can be either a string literal
or a constant name for a static str
.
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)]
struct Options {
#[bpaf(short, argument("NUM"), guard(dlc_check, DLC_NEEDED))]
number: u32,
}
Example
$ app -n 100
// fails with "Values greater than 10 are only available in the DLC pack!"
$ app -n 5
// 5
Examples found in repository?
More examples
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
fn user() -> impl Parser<Option<String>> {
let tag = any::<String>("TAG")
.guard(|s| s == "-user", "not user")
.hide();
let value = positional::<String>("USER");
construct!(tag, value)
.anywhere()
.map(|pair| pair.1)
.optional()
.catch()
}
// parsers -exec xxx yyy zzz ;
fn exec() -> impl Parser<Option<Vec<OsString>>> {
let tag = any::<String>("-exec")
.help("-exec /path/to/command flags and options ;")
.guard(|s| s == "-exec", "not find");
let item = any::<OsString>("ITEM")
.guard(|s| s != ";", "not word")
.many()
.catch()
.hide();
let endtag = any::<String>("END").guard(|s| s == ";", "not eot").hide();
construct!(tag, item, endtag)
.anywhere()
.map(|triple| triple.1)
.optional()
.catch()
}
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
fn main() {
// defining a parser in a usual way
let width = short('w').argument::<usize>("WIDTH").fallback(10);
let height = short('h').argument::<usize>("HEIGHT").fallback(10);
let parser = construct!(Opts { width, height });
let cmd = positional::<String>("")
.guard(|s| s == "cmd", "")
.optional()
.hide();
let combined_parser = construct!(cmd, parser).map(|x| x.1);
let opts = combined_parser.to_options().run();
println!("{:?}", opts);
}
Use this value as default if value isn’t present on a command line
Parser would still fail if value is present but failure comes from some transformation
Combinatoric usage
fn number() -> impl Parser<u32> {
short('n')
.argument::<u32>("NUM")
.fallback(42)
}
Derive usage
Expression in parens should have the right type, this example uses u32
literal,
but it can also be your own type if that’s what you are parsing, it can also be a function
call.
#[derive(Debug, Clone, Bpaf)]
struct Options {
#[bpaf(short, argument("NUM"), fallback(42))]
number: u32
}
Example
$ app -n 100
// 10
$ app
// 42
$ app -n pi
// fails with "Couldn't parse "pi": invalid numeric literal"
See also
fallback_with
would allow to try to fallback to a value that
comes from a failing computation such as reading a file.
Examples found in repository?
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
fn in_file() -> impl Parser<String> {
tag::<String>("if=", "if=FILE", "read from FILE instead of stdin").fallback(String::from("-"))
}
fn out_file() -> impl Parser<String> {
tag::<String>("of=", "of=FILE", "write to FILE instead of stdout").fallback(String::from("-"))
}
fn block_size() -> impl Parser<usize> {
// it is possible to parse notation used by dd itself as well, using only ints for simplicity
tag::<usize>(
"bs=",
"bs=SIZE",
"read/write SIZE blocks at once instead of 512",
)
.fallback(512)
}
More examples
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
pub fn options() -> OptionParser<Options> {
let backing = toggle_options("backing", "Backing status").fallback(false);
let xinerama = toggle_options("xinerama", "Xinerama status").fallback(true);
let turbo = short('t')
.long("turbo")
.help("Engage the turbo mode")
.switch();
let extensions = extension().many();
construct!(Options {
turbo,
backing,
xinerama,
extensions,
})
.to_options()
}
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
fn main() {
// defining a parser in a usual way
let width = short('w').argument::<usize>("WIDTH").fallback(10);
let height = short('h').argument::<usize>("HEIGHT").fallback(10);
let parser = construct!(Opts { width, height });
let cmd = positional::<String>("")
.guard(|s| s == "cmd", "")
.optional()
.hide();
let combined_parser = construct!(cmd, parser).map(|x| x.1);
let opts = combined_parser.to_options().run();
println!("{:?}", opts);
}
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
fn main() {
// A flag, true if used in the command line. Can be required, this one is optional
let debug = bpaf::short('d')
.long("debug")
.help("Activate debug mode")
.switch();
// an argument, parsed and with default value
let speed = bpaf::Parser::fallback(
bpaf::short('s')
.long("speed")
.help("Set speed")
.argument::<f64>("SPEED"),
42.0,
);
// packing things in a struct assumes parser for each field is in scope.
let opt = bpaf::Parser::to_options(bpaf::construct!(Out { debug, speed })).run();
println!("{:#?}", opt);
}
Use value produced by this function as default if value isn’t present
Would still fail if value is present but failure comes from some earlier transformation
Combinatoric usage
fn username() -> impl Parser<String> {
long("user")
.argument::<String>("USER")
.fallback_with::<_, Box<dyn std::error::Error>>(||{
let output = std::process::Command::new("whoami")
.stdout(std::process::Stdio::piped())
.spawn()?
.wait_with_output()?
.stdout;
Ok(std::str::from_utf8(&output)?.to_owned())
})
}
Derive usage
fn get_current_user() -> Result<String, Box<dyn std::error::Error>> {
let output = std::process::Command::new("whoami")
.stdout(std::process::Stdio::piped())
.spawn()?
.wait_with_output()?
.stdout;
Ok(std::str::from_utf8(&output)?.to_owned())
}
#[derive(Debug, Clone, Bpaf)]
struct Options {
#[bpaf(long, argument("USER"), fallback_with(get_current_user))]
user: String,
}
Example
$ app --user bobert
// "bobert"
$ app
// "pacak"
See also
fallback
implements similar logic expect that failures
aren’t expected.
Examples found in repository?
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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
fn parse_manifest_path() -> impl Parser<PathBuf> {
long("manifest-path")
.help("Path to Cargo.toml")
.argument::<PathBuf>("PATH")
.parse(|p| {
if p.is_absolute() {
Ok(p)
} else {
std::env::current_dir()
.map(|d| d.join(p))
.and_then(|full_path| full_path.canonicalize())
}
})
.fallback_with(|| std::env::current_dir().map(|x| x.join("Cargo.toml")))
}
#[derive(Debug, Clone, Bpaf)]
pub struct Format {
/// Print interleaved Rust code
pub rust: bool,
#[bpaf(external(color_detection))]
pub color: bool,
/// include full demangled name instead of just prefix
pub full_name: bool,
}
#[derive(Debug, Clone, Bpaf)]
pub enum Syntax {
/// Generate assembly using Intel style
Intel,
/// Generate assembly using AT&T style
Att,
}
impl ToString for Syntax {
fn to_string(&self) -> String {
match self {
Syntax::Intel => String::from("llvm-args=-x86-asm-syntax=intel"),
Syntax::Att => String::from("llvm-args=-x86-asm-syntax=att"),
}
}
}
fn color_detection() -> impl Parser<bool> {
let yes = long("color")
.help("Enable color highlighting")
.req_flag(true);
let no = long("no-color")
.help("Disable color highlighting")
.req_flag(false);
construct!([yes, no]).fallback_with::<_, &str>(|| Ok(true))
}
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 usage
/// bpaf would accept both `-W` and `-H` flags, but the help message
/// would contain only `-H`
fn rectangle() -> impl Parser<(u32, u32)> {
let width = short('W')
.argument::<u32>("PX")
.fallback(10)
.hide();
let height = short('H')
.argument::<u32>("PX")
.fallback(10)
.hide();
construct!(width, height)
}
Example
$ app -W 12 -H 15
// (12, 15)
$ app -H 333
// (10, 333)
$ app --help
// contains -H but not -W
Derive usage
#[derive(Debug, Clone, Bpaf)]
struct Rectangle {
#[bpaf(short('W'), argument("PX"), fallback(10), hide)]
width: u32,
#[bpaf(short('H'), argument("PX"))]
height: u32,
}
Example
$ app -W 12 -H 15
// Rectangle { width: 12, height: 15 }
$ app -H 333
// Rectangle { width: 10, height: 333 }
$ app --help
// contains -H but not -W
Examples found in repository?
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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
fn user() -> impl Parser<Option<String>> {
let tag = any::<String>("TAG")
.guard(|s| s == "-user", "not user")
.hide();
let value = positional::<String>("USER");
construct!(tag, value)
.anywhere()
.map(|pair| pair.1)
.optional()
.catch()
}
// parsers -exec xxx yyy zzz ;
fn exec() -> impl Parser<Option<Vec<OsString>>> {
let tag = any::<String>("-exec")
.help("-exec /path/to/command flags and options ;")
.guard(|s| s == "-exec", "not find");
let item = any::<OsString>("ITEM")
.guard(|s| s != ";", "not word")
.many()
.catch()
.hide();
let endtag = any::<String>("END").guard(|s| s == ";", "not eot").hide();
construct!(tag, item, endtag)
.anywhere()
.map(|triple| triple.1)
.optional()
.catch()
}
/// parses symbolic permissions `-perm -mode`, `-perm /mode` and `-perm mode`
fn perm() -> impl Parser<Option<Perm>> {
fn parse_mode(input: &str) -> Result<Perms, String> {
let mut perms = Perms::default();
for c in input.chars() {
match c {
'r' => perms.read = true,
'w' => perms.write = true,
'x' => perms.exec = true,
_ => return Err(format!("{} is not a valid permission string", input)),
}
}
Ok(perms)
}
let tag = any::<String>("-mode").help("-mode (perm | -perm | /perm)");
let mode = any::<String>("mode")
.parse::<_, _, String>(|s| {
if let Some(m) = s.strip_prefix('-') {
Ok(Perm::All(parse_mode(m)?))
} else if let Some(m) = s.strip_prefix('/') {
Ok(Perm::Any(parse_mode(m)?))
} else {
Ok(Perm::Exact(parse_mode(&s)?))
}
})
.hide();
construct!(tag, mode)
.anywhere()
.map(|pair| pair.1)
.optional()
.catch()
}
More examples
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
fn main() {
// defining a parser in a usual way
let width = short('w').argument::<usize>("WIDTH").fallback(10);
let height = short('h').argument::<usize>("HEIGHT").fallback(10);
let parser = construct!(Opts { width, height });
let cmd = positional::<String>("")
.guard(|s| s == "cmd", "")
.optional()
.hide();
let combined_parser = construct!(cmd, parser).map(|x| x.1);
let opts = combined_parser.to_options().run();
println!("{:?}", opts);
}
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
fn extension() -> impl Parser<(String, bool)> {
let on = any::<String>("+ext")
.help("enable ext <EXT>")
.parse::<_, _, String>(|s| {
if s == "+ext" {
Ok(true)
} else {
Err(String::new())
}
});
let off = any::<String>("-ext")
.help("disable ext <EXT>")
.parse::<_, _, String>(|s| {
if s == "-ext" {
Ok(false)
} else {
Err(String::new())
}
});
let state = construct!([on, off]);
let name = positional::<String>("EXT").hide();
construct!(state, name).map(|(a, b)| (b, a)).anywhere()
}
sourcefn group_help(self, message: &'static str) -> ParseGroupHelp<Self>where
Self: Sized + Parser<T>,
fn group_help(self, message: &'static str) -> ParseGroupHelp<Self>where
Self: Sized + Parser<T>,
Attach 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 usage
fn rectangle() -> impl Parser<(u32, u32)> {
let width = short('w')
.argument::<u32>("PX");
let height = short('h')
.argument::<u32>("PX");
construct!(width, height)
.group_help("Takes a rectangle")
}
Example
$ app --help
<skip>
Takes a rectangle
-w <PX> Width of the rectangle
-h <PX> Height of the rectangle
<skip>
Derive usage
#[derive(Debug, Clone, Bpaf)]
struct Rectangle {
width: u32,
height: u32,
}
#[derive(Debug, Clone, Bpaf)]
struct Options {
#[bpaf(external, group_help("Takes a rectangle"))]
rectangle: Rectangle
}
Examples found in repository?
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);
}
Dynamic shell completion
Allows to generate autocompletion information for shell. Completer places generated input
in place of metavar placeholders, so running completer
on something that doesn’t have a
positional
or an argument
(or their _os
variants) doesn’t make
much sense.
Takes a function as a parameter that tries to complete partial input to a full one with
optional description. bpaf
would substitute current positional item or an argument an empty
string if a value isn’t available yet so it’s best to run complete
where parsing can’t fail:
right after argument
or positional
, but this isn’t enforced.
bpaf
doesn’t support generating OsString
completions: bpaf
must
print completions to console and for non-string values it’s not possible (accurately).
Using this function requires enabling "autocomplete"
feature, not enabled by default.
Example
$ app --name L<TAB>
$ app --name Lupusregina _
Combinatoric usage
fn completer(input: &String) -> Vec<(&'static str, Option<&'static str>)> {
let names = ["Yuri", "Lupusregina", "Solution", "Shizu", "Entoma"];
names
.iter()
.filter(|name| name.starts_with(input))
.map(|name| (*name, None))
.collect::<Vec<_>>()
}
fn name() -> impl Parser<String> {
short('n')
.long("name")
.help("Specify character's name")
.argument::<String>("Name")
.complete(completer)
}
Derive usage
fn completer(input: &String) -> Vec<(&'static str, Option<&'static str>)> {
let names = ["Yuri", "Lupusregina", "Solution", "Shizu", "Entoma"];
names
.iter()
.filter(|name| name.starts_with(input))
.map(|name| (*name, None))
.collect::<Vec<_>>()
}
#[derive(Debug, Clone, Bpaf)]
struct Options {
#[bpaf(argument("NAME"), complete(completer))]
name: String,
}
Examples found in repository?
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
fn main() {
use bpaf::*;
let a = short('a').long("avocado").help("Use avocado").switch();
let b = short('b').long("banana").help("Use banana").switch();
let bb = long("bananananana").help("I'm Batman").switch();
let c = long("calculator")
.help("calculator expression")
.argument::<String>("EXPR")
.complete(complete_calculator);
let parser = construct!(a, b, bb, c)
.to_options()
.descr("Dynamic autocomplete example")
.footer(
"\
bpaf supports dynamic autocompletion for a few shells, make sure your binary is in $PATH
and try using one of those this output should go into a file that depends on your shell:
$ csample --bpaf-complete-style-bash
$ csample --bpaf-complete-style-zsh
$ csample --bpaf-complete-style-fish
$ csample --bpaf-complete-style-elvish",
);
println!("{:?}", parser.run());
}
sourcefn complete_style(self, style: CompleteDecor) -> ParseCompStyle<Self>where
Self: Sized + Parser<T>,
fn complete_style(self, style: CompleteDecor) -> ParseCompStyle<Self>where
Self: Sized + Parser<T>,
Add extra annotations to completion information
Not all information is gets supported by all the shells
Combinatoric usage
fn opts() -> impl Parser<(bool, bool)> {
let a = short('a').switch();
let b = short('b').switch();
let c = short('c').switch();
let d = short('d').switch();
let ab = construct!(a, b).complete_style(CompleteDecor::VisibleGroup("a and b"));
let cd = construct!(c, d).complete_style(CompleteDecor::VisibleGroup("c and d"));
construct!([ab, cd])
}
Automagically restrict the inner parser scope to accept adjacent values only
adjacent
can solve surprisingly wide variety of problems: sequential command chaining,
multi-value arguments, option-structs to name a few. If you want to run a parser on a
sequential subset of arguments - adjacent
might be able to help you. Check the examples
for better intuition.
Multi-value arguments
Parsing things like --foo ARG1 ARG2 ARG3
Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
multi: Vec<Multi>,
switch: bool,
}
#[derive(Debug, Clone)]
struct Multi {
m: (),
val_1: usize,
val_2: usize,
val_3: f64,
}
fn multi() -> impl Parser<Multi> {
let m = short('m').req_flag(());
let val_1 = positional::<usize>("V1");
let val_2 = positional::<usize>("V2");
let val_3 = positional::<f64>("V3");
construct!(Multi {
m,
val_1,
val_2,
val_3
})
.adjacent()
}
pub fn options() -> OptionParser<Options> {
let switch = short('s').switch();
let multi = multi().many();
construct!(Options { multi, switch }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
#[bpaf(external, many)]
multi: Vec<Multi>,
#[bpaf(short)]
switch: bool,
}
#[derive(Debug, Clone, Bpaf)]
#[bpaf(adjacent)]
struct Multi {
m: (),
#[bpaf(positional("V1"))]
val_1: usize,
#[bpaf(positional("V2"))]
val_2: usize,
#[bpaf(positional("V3"))]
val_3: f64,
}
Examples
short flag -m
takes 3 positional arguments: two integers and one floating point, order is
important, switch -s
can go on either side of it
% app -s -m 10 20 3.1415
Options { multi: [Multi { m: (), val_1: 10, val_2: 20, val_3: 3.1415 }], switch: true }
parser accepts multiple groups of -m
- they must not interleave
% app -s -m 10 20 3.1415 -m 1 2 0.0
Options { multi: [Multi { m: (), val_1: 10, val_2: 20, val_3: 3.1415 }, Multi { m: (), val_1: 1, val_2: 2, val_3: 0.0 }], switch: true }
-s
can’t go in the middle
% app -m 10 20 -s 3.1415
-m is not expected in this context
Structure groups
Parsing things like --foo --foo-1 ARG1 --foo-2 ARG2 --foo-3 ARG3
Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
switch: bool,
multi: Vec<Rect>,
}
#[derive(Debug, Clone)]
struct Rect {
item: (),
width: usize,
height: usize,
painted: bool,
}
fn multi() -> impl Parser<Rect> {
let item = long("rect").req_flag(());
let width = long("width").argument::<usize>("PX");
let height = long("height").argument::<usize>("PX");
let painted = long("painted").switch();
construct!(Rect {
item,
width,
height,
painted,
})
.adjacent()
}
pub fn options() -> OptionParser<Options> {
let switch = short('s').switch();
let multi = multi().many();
construct!(Options { multi, switch }).to_options()
}
Examples
Order of items within the rectangle is not significant and you can have several of them
% app --rect --width 10 --height 10 --rect --height 10 --width 10
Options { switch: false, multi: [Rect { item: (), width: 10, height: 10, painted: false }, Rect { item: (), width: 10, height: 10, painted: false }] }
You can have optional values that belong to the group inside and outer flags in the middle
% app --rect --width 10 --painted --height 10 -s --rect --height 10 --width 10
Options { switch: true, multi: [Rect { item: (), width: 10, height: 10, painted: true }, Rect { item: (), width: 10, height: 10, painted: false }] }
But with adjacent
they cannot interleave
% app --rect --rect --width 10 --painted --height 10 --height 10 --width 10
--rect is not expected in this context
Chaining commands
Parsing things like cmd1 --arg1 cmd2 --arg2 --arg3 cmd3 --flag
Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
switch: bool,
commands: Vec<Cmd>,
}
#[derive(Debug, Clone)]
enum Cmd {
Eat(String),
Drink(bool),
Sleep(usize),
}
fn cmd() -> impl Parser<Cmd> {
let eat = positional::<String>("FOOD")
.to_options()
.command("eat")
.adjacent()
.map(Cmd::Eat);
let drink = long("coffee")
.switch()
.to_options()
.command("drink")
.adjacent()
.map(Cmd::Drink);
let sleep = long("time")
.argument::<usize>("HOURS")
.to_options()
.command("sleep")
.adjacent()
.map(Cmd::Sleep);
construct!([eat, drink, sleep])
}
pub fn options() -> OptionParser<Options> {
let switch = short('s').switch();
let commands = cmd().many();
construct!(Options { commands, switch }).to_options()
}
Examples
You can chain one or more commands, commands can be arbitrarily nested too
% app eat fastfood drink --coffee sleep --time=5
Options { switch: false, commands: [Eat("fastfood"), Drink(true), Sleep(5)] }
You can pass other flags after all the commands but not in between them
since commands are treated as positionals. It should be possible to consume
items before and between commands as well if they are consumed before the commands
like this: construct!(Options { switch, commands })
but in that case you need
to be careful about not consuming anything from the command themselves.
% app sleep --time 10 eat "Bak Kut Teh" drink -s
Options { switch: true, commands: [Sleep(10), Eat("Bak Kut Teh"), Drink(false)] }
Start and end markers
Parsing things like find . --exec foo {} -bar ; --more
Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
switch: bool,
exec: Vec<OsString>,
}
fn exec() -> impl Parser<Vec<OsString>> {
let start = long("exec").req_flag(());
let body = any::<OsString>("EXEC")
.guard(|s| s != ";", "end marker")
.many()
.catch();
let end = any::<OsString>("TAIL").guard(|s| s == ";", "end marker");
construct!(start, body, end).adjacent().map(|x| x.1)
}
pub fn options() -> OptionParser<Options> {
let switch = short('s').switch();
let exec = exec();
construct!(Options { exec, switch }).to_options()
}
Examples
You can have as many items between --exec
and ;
as you want, they all will be captured
inside the exec vector. Extra options can go either before or after the block.
% app --exec foo --bar ; -s
Options { switch: true, exec: ["foo", "--bar"] }
Multi-value arguments with optional flags
Parsing things like --foo ARG1 --flag --inner ARG2
So you can parse things while parsing things. Not sure why you might need this, but you can :)
Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
multi: Vec<Multi>,
switch: bool,
}
#[derive(Debug, Clone)]
struct Multi {
m: (),
pos: usize,
flag: bool,
arg: Option<usize>,
}
/// You can mix all sorts of things inside the adjacent group
fn multi() -> impl Parser<Multi> {
let m = short('m').req_flag(());
let pos = positional::<usize>("POS");
let arg = long("arg").argument::<usize>("ARG").optional();
let flag = long("flag").switch();
construct!(Multi { m, arg, flag, pos }).adjacent()
}
pub fn options() -> OptionParser<Options> {
let switch = short('s').switch();
let multi = multi().many();
construct!(Options { multi, switch }).to_options()
}
Examples
Let’s start simple - a single flag accepts a bunch of stuff, and eveything is present
% app -m 330 --arg 10 --flag
Options { multi: [Multi { m: (), pos: 330, flag: true, arg: Some(10) }], switch: false }
You can omit some parts, but also have multiple groups thank to many
% app -m 100 --flag -m 30 --arg 10 -m 50
Options { multi: [Multi { m: (), pos: 100, flag: true, arg: None }, Multi { m: (), pos: 30, flag: false, arg: Some(10) }, Multi { m: (), pos: 50, flag: false, arg: None }], switch: false }
Performance and other considerations
bpaf
can run adjacently restricted parsers multiple times to refine the guesses. It’s
best not to have complex inter-fields verification since they might trip up the detection
logic: instead of destricting, for example “sum of two fields to be 5 or greater” inside the
adjacent
parser, you can restrict it outside, once adjacent
done the parsing.
adjacent
is available on a trait for better discoverability, it doesn’t make much sense to
use it on something other than command
or construct!
encasing
several fields.
There’s also similar method adjacent
that allows to restrict argument
parser to work only for arguments where both key and a value are in the same shell word:
-f=bar
or -fbar
, but not -f bar
.
Examples found in repository?
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
fn opts() -> Opts {
let sensor = long("sensor").req_flag(());
let device = long("sensor-device").argument::<String>("DEVICE");
let name = long("sensor-name").argument::<String>("NAME");
// from_str needs to be replaced with `parse` that can deal with hex digits
let bus_id = long("sensor-i2c-bus").argument::<usize>("BUS");
let address = long("sensor-i2c-address").argument::<usize>("ADDRESS");
let sensors = construct!(Sensor {
sensor,
device,
name,
bus_id,
address
})
.adjacent()
.many();
construct!(Opts { sensors }).to_options().run()
}
Parse anywhere
Most generic escape hatch available, in combination with any
allows to parse anything
anywhere, works by repeatedly trying to run the inner parser on each subsequent context.
Can be expensive performance wise especially if parser contains complex logic.
Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
turbo: bool,
backing: bool,
xinerama: bool,
}
fn toggle_options(name: &'static str, help: &'static str) -> impl Parser<bool> {
any::<String>(name)
.help(help)
.parse(move |s| {
let (state, cur_name) = if let Some(rest) = s.strip_prefix('+') {
(true, rest)
} else if let Some(rest) = s.strip_prefix('-') {
(false, rest)
} else {
return Err(format!("{} is not a toggle option", s));
};
if cur_name != name {
Err(format!("{} is not a known toggle option name", cur_name))
} else {
Ok(state)
}
})
.anywhere()
}
pub fn options() -> OptionParser<Options> {
let backing = toggle_options("backing", "Backing status").fallback(false);
let xinerama = toggle_options("xinerama", "Xinerama status").fallback(true);
let turbo = short('t')
.long("turbo")
.help("Engage the turbo mode")
.switch();
construct!(Options {
turbo,
backing,
xinerama,
})
.to_options()
}
Examples
This example shows how to parse some very unusual options, same style as used by Xorg
-backing
disables backing +backing
enables it, usual restrictions and combinations apply:
fails if present more than once, can be further transformed with combinators.
By default xinerama
is enabled, anything else is disabled
% app
Options { turbo: false, backing: false, xinerama: true }
Strange things we added can be mixed with the regular options
% app --turbo +backing -xinerama
Options { turbo: true, backing: true, xinerama: false }
As expected - order doesn’t matter
% app +backing --turbo
Options { turbo: true, backing: true, xinerama: true }
–help will try to render it but you can always .hide
it and add your own lines
with .header
or .footer
methods on OptionParser
.
% app --help
Usage: [-t] [<backing>] [<xinerama>]
Available positional items:
<backing> Backing status
<xinerama> Xinerama status
Available options:
-t, --turbo Engage the turbo mode
-h, --help Prints help information
Examples found in repository?
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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
fn user() -> impl Parser<Option<String>> {
let tag = any::<String>("TAG")
.guard(|s| s == "-user", "not user")
.hide();
let value = positional::<String>("USER");
construct!(tag, value)
.anywhere()
.map(|pair| pair.1)
.optional()
.catch()
}
// parsers -exec xxx yyy zzz ;
fn exec() -> impl Parser<Option<Vec<OsString>>> {
let tag = any::<String>("-exec")
.help("-exec /path/to/command flags and options ;")
.guard(|s| s == "-exec", "not find");
let item = any::<OsString>("ITEM")
.guard(|s| s != ";", "not word")
.many()
.catch()
.hide();
let endtag = any::<String>("END").guard(|s| s == ";", "not eot").hide();
construct!(tag, item, endtag)
.anywhere()
.map(|triple| triple.1)
.optional()
.catch()
}
/// parses symbolic permissions `-perm -mode`, `-perm /mode` and `-perm mode`
fn perm() -> impl Parser<Option<Perm>> {
fn parse_mode(input: &str) -> Result<Perms, String> {
let mut perms = Perms::default();
for c in input.chars() {
match c {
'r' => perms.read = true,
'w' => perms.write = true,
'x' => perms.exec = true,
_ => return Err(format!("{} is not a valid permission string", input)),
}
}
Ok(perms)
}
let tag = any::<String>("-mode").help("-mode (perm | -perm | /perm)");
let mode = any::<String>("mode")
.parse::<_, _, String>(|s| {
if let Some(m) = s.strip_prefix('-') {
Ok(Perm::All(parse_mode(m)?))
} else if let Some(m) = s.strip_prefix('/') {
Ok(Perm::Any(parse_mode(m)?))
} else {
Ok(Perm::Exact(parse_mode(&s)?))
}
})
.hide();
construct!(tag, mode)
.anywhere()
.map(|pair| pair.1)
.optional()
.catch()
}
More examples
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
fn tag<T>(name: &'static str, meta: &'static str, help: &'static str) -> impl Parser<T>
where
T: FromStr,
<T as std::str::FromStr>::Err: std::fmt::Display,
{
// it is possible to parse OsString here and strip the prefix with os_str_bytes or a similar
// crate
any::<String>(meta)
.help(help)
.parse::<_, _, String>(move |s| match s.strip_prefix(name) {
None => Err("Wrong tag".to_string()),
Some(body) => T::from_str(body).map_err(|e| e.to_string()),
})
.anywhere()
}
11 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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
fn toggle_options(name: &'static str, help: &'static str) -> impl Parser<bool> {
any::<String>(name)
.help(help)
.parse(move |s| {
let (state, cur_name) = if let Some(rest) = s.strip_prefix('+') {
(true, rest)
} else if let Some(rest) = s.strip_prefix('-') {
(false, rest)
} else {
return Err(format!("{} is not a toggle option", s));
};
if cur_name != name {
Err(format!("{} is not a known toggle option name", cur_name))
} else {
Ok(state)
}
})
.anywhere()
}
fn extension() -> impl Parser<(String, bool)> {
let on = any::<String>("+ext")
.help("enable ext <EXT>")
.parse::<_, _, String>(|s| {
if s == "+ext" {
Ok(true)
} else {
Err(String::new())
}
});
let off = any::<String>("-ext")
.help("disable ext <EXT>")
.parse::<_, _, String>(|s| {
if s == "-ext" {
Ok(false)
} else {
Err(String::new())
}
});
let state = construct!([on, off]);
let name = positional::<String>("EXT").hide();
construct!(state, name).map(|(a, b)| (b, a)).anywhere()
}
sourcefn to_options(self) -> OptionParser<T>where
Self: Sized + Parser<T> + 'static,
fn to_options(self) -> OptionParser<T>where
Self: Sized + Parser<T> + 'static,
Transform Parser
into OptionParser
to attach metadata and run
Combinatoric usage
fn parser() -> impl Parser<u32> {
short('i')
.argument::<u32>("ARG")
}
fn option_parser() -> OptionParser<u32> {
parser()
.to_options()
.version("3.1415")
.descr("This is a description")
}
See OptionParser
for more methods available after conversion.
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. Former uses version from cargo
, later uses the
specified value which should be an expression of type &'static str
, see
version
.
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options, version("3.1415"))]
/// This is a description
struct Options {
verbose: bool,
}
Example
$ app --version
// Version: 3.1415
$ app --help
<skip>
This is a description
<skip>
Examples found in repository?
More examples
- examples/top_to_bottom.rs
- examples/numeric_prefix.rs
- examples/customize_help.rs
- examples/flatten.rs
- examples/enum_tuple.rs
- examples/xorg.rs
- examples/negative.rs
- examples/cargo-cmd.rs
- examples/sensors.rs
- examples/no_import.rs
- examples/multiple_fallback.rs
- examples/cat.rs
- examples/rectangle.rs
- examples/git.rs
- examples/csample.rs
- examples/confusing.rs
- examples/verbose.rs
- examples/basic.rs
- examples/travel.rs