Expand description
Tools to define primitive parsers
Ways to consume data
Flag
flag
- a string that consists of two dashes (--flag
) and a name and a single dash and a single character (-f
) created withlong
andshort
respectively. Depending if this name is present or absent on the command line primitive flag parser produces one of two values. User can combine several short flags in a single invocation:-a -b -c
is the same as-abc
.
Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
decision: Decision,
}
#[derive(Debug, Clone)]
pub enum Decision {
Yes,
No,
}
fn parse_decision() -> impl Parser<Decision> {
long("decision")
.help("Positive decision")
.flag(Decision::Yes, Decision::No)
}
pub fn options() -> OptionParser<Options> {
let decision = parse_decision();
construct!(Options { decision }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
#[bpaf(flag(Decision::Yes, Decision::No))]
decision: Decision,
}
#[derive(Debug, Clone)]
pub enum Decision {
Yes,
No,
}
Examples
Presense of a long name is decoded into Yes
% app --decision
Options { decision: Yes }
Absense is No
% app
Options { decision: No }
Required flag
Similar to flag
, but instead of falling back to the second value required flag parser would
fail. Mostly useful in combination with other parsers, created with NamedArg::req_flag
.
Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
decision: Decision,
}
#[derive(Debug, Clone)]
pub enum Decision {
On,
Off,
Undecided,
}
// user can specify either --on or --off, parser would fallback to `Undecided`
fn parse_decision() -> impl Parser<Decision> {
let on = long("on").help("Positive decision").req_flag(Decision::On);
let off = long("off")
.help("Negative decision")
.req_flag(Decision::Off);
construct!([on, off]).fallback(Decision::Undecided)
}
pub fn options() -> OptionParser<Options> {
let decision = parse_decision();
construct!(Options { decision }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
#[bpaf(external)]
decision: Decision,
}
#[derive(Debug, Clone, Bpaf)]
#[bpaf(fallback(Decision::Undecided))]
pub enum Decision {
/// Positive decision
On,
/// Negative decision
Off,
#[bpaf(skip)]
Undecided,
}
Examples
This example implements a tri-state switch: wether decision was made
positive, negative or not at all. Alternative implementation can use
optional
and None
to indicate “no decision” case.
A case with positive decision:
% app --on
Options { decision: On }
A case with no decision:
% app
Options { decision: Undecided }
--on
and --off
are mutually exclusive:
% app --on --off
--off is not expected in this context: --off cannot be used at the same time as --on
help
% app --help
Usage: [--on | --off]
Available options:
--on Positive decision
--off Negative decision
-h, --help Prints help information
Switch
A special case of a flag that gets decoded into a bool
, mostly serves as a convenient
shortcut to .flag(true, false)
. Created with NamedArg::switch
.
Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
decision: bool,
}
fn parse_decision() -> impl Parser<bool> {
long("decision").help("Positive decision").switch()
}
pub fn options() -> OptionParser<Options> {
let decision = parse_decision();
construct!(Options { decision }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
decision: bool,
}
Examples
Presense of a long name is decoded into true
% app --decision
Options { decision: true }
Absense is false
% app
Options { decision: false }
Argument
A short or long flag
followed by either a space or =
and
then by a string literal. -f foo
, --flag bar
or -o=-
are all valid argument examples. Note, string
literal can’t start with -
unless separated from the flag with =
. For short flags value
can follow immediately: -fbar
.
Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
value: isize,
shorty: u64,
}
pub fn options() -> OptionParser<Options> {
let value = long("value").argument::<isize>("ARG").fallback(100);
// in many cases rustc is able to deduct exact type for the argument
// you are trying to consume, alternatively you can always specify it
// with turbofish to `argument:`
// let shorty = short('s').argument::<u64>("ARG");
let shorty = short('s').argument("ARG");
construct!(Options { value, shorty }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
#[bpaf(fallback(100))]
value: isize,
// in many cases rustc is able to deduct exact type for the argument
// you are trying to consume, alternatively you can always specify it
// with turbofish to `argument:`
// #[bpaf(short, argument::<u64>("ARG"))]
#[bpaf(short, argument("ARG"))]
shorty: u64,
}
Examples
Names for arguments could be short or long, and they can be parsed as mutiple different
types, this example uses isize
and u64
% app --value 50 -s=18446744073709551615
Options { value: 50, shorty: 18446744073709551615 }
Value can be separated from the flag by space, =
or for short ones - be immediately adjacent
% app --value=1 -s42
Options { value: 1, shorty: 42 }
You can apply fallback and other transformation
% app -s0
Options { value: 100, shorty: 0 }
But if there’s no fallback - the value is required
% app --value 1
Expected -s ARG, pass --help for usage information
Argument is required
% app -s
-s requires an argument
Positional
A positional argument with no additonal name, for example in vim main.rs
main.rs
is a positional argument. Can’t start with -
, created with positional
.
Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
coin: Coin,
file: PathBuf,
name: Option<String>,
}
/// A custom datatype that implements [`FromStr`]
#[derive(Debug, Clone, Copy)]
enum Coin {
Heads,
Tails,
}
impl FromStr for Coin {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"heads" => Ok(Coin::Heads),
"tails" => Ok(Coin::Tails),
_ => Err(format!("Expected 'heads' or 'tails', got '{}'", s)),
}
}
}
pub fn options() -> OptionParser<Options> {
let file = positional::<PathBuf>("FILE").help("File to use");
// sometimes you can get away with not specifying type in positional's turbofish
let coin = long("coin")
.help("Coin toss results")
.argument::<Coin>("COIN")
.fallback(Coin::Heads);
let name = positional::<String>("NAME")
.help("Name to look for")
.optional();
construct!(Options { coin, file, name }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
/// Coin toss results
#[bpaf(argument("COIN"), fallback(Coin::Heads))]
coin: Coin,
/// File to use
#[bpaf(positional::<PathBuf>("FILE"))]
file: PathBuf,
/// Name to look for
#[bpaf(positional("NAME"))]
name: Option<String>,
}
/// A custom datatype that implements [`FromStr`]
#[derive(Debug, Clone, Copy)]
enum Coin {
Heads,
Tails,
}
impl FromStr for Coin {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"heads" => Ok(Coin::Heads),
"tails" => Ok(Coin::Tails),
_ => Err(format!("Expected 'heads' or 'tails', got '{}'", s)),
}
}
}
Examples
Positionals are consumed left to right, one at a time, no skipping unless the value is optional
% app main.rs
Options { coin: Heads, file: "main.rs", name: None }
Both positionals are present
% app main.rs hello
Options { coin: Heads, file: "main.rs", name: Some("hello") }
You can consume items of any type that implements FromStr
.
% app main.rs --coin tails
Options { coin: Tails, file: "main.rs", name: None }
Only name
is optional in this example, not specifying file
is a failure
% app
Expected <FILE>, pass --help for usage information
And usage information
% app --help
Usage: [--coin COIN] <FILE> [<NAME>]
Available positional items:
<FILE> File to use
<NAME> Name to look for
Available options:
--coin <COIN> Coin toss results
-h, --help Prints help information
Any
Also a positional argument with no additional name, but unlike positional
itself, any
isn’t restricted to positional looking structure and would consume any items as they appear on
a command line. Can be useful to collect anything unused to pass to other applications.
Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
turbo: bool,
rest: Vec<OsString>,
}
pub fn options() -> OptionParser<Options> {
let turbo = short('t')
.long("turbo")
.help("Engage the turbo mode")
.switch();
let rest = any::<OsString>("REST")
.help("app will pass anything unused to a child process")
.guard(|x| x != "--help", "keep help")
.many();
construct!(Options { turbo, rest }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
#[bpaf(short, long)]
/// Engage the turbo mode
turbo: bool,
#[bpaf(any("REST"), guard(not_help, "keep help"), many)]
/// app will pass anything unused to a child process
rest: Vec<OsString>,
}
fn not_help(s: &OsString) -> bool {
s != "--help"
}
Examples
Capture --turbo
flag for internal use and return everything else as is so it can be passed
to some other program. Anything except for --turbo
here and in following examples is
consumed by any
% app --turbo git commit -m "hello world"
Options { turbo: true, rest: ["git", "commit", "-m", "hello world"] }
Or just capture and return everything
% app git commit -m "hello world"
Options { turbo: false, rest: ["git", "commit", "-m", "hello world"] }
Doesn’t have to be in order either
% app git commit -m="hello world" --turbo
Options { turbo: true, rest: ["git", "commit", "-m=hello world"] }
You can keep --help
working, but you need to add extra guard
for that
% app --turbo --help
Usage: [-t] <REST>...
Available positional items:
<REST> app will pass anything unused to a child process
Available options:
-t, --turbo Engage the turbo mode
-h, --help Prints help information
Command
A command defines a starting point for an independent subparser. Name must be a valid utf8
string. For example cargo build
invokes command "build"
and after "build"
cargo
starts accepting values it won’t accept otherwise
Combinatoric usage
#[derive(Debug, Clone)]
pub struct Cmd {
flag: bool,
arg: usize,
}
#[derive(Debug, Clone)]
pub struct Options {
flag: bool,
cmd: Cmd,
}
fn cmd() -> impl Parser<Cmd> {
let flag = long("flag")
.help("This flag is specific to command")
.switch();
let arg = long("arg").argument::<usize>("ARG");
construct!(Cmd { flag, arg })
.to_options()
.descr("Command to do something")
.command("cmd")
.help("Command to do something")
}
pub fn options() -> OptionParser<Options> {
let flag = long("flag")
.help("This flag is specific to the outer layer")
.switch();
construct!(Options { flag, cmd() }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(command)]
/// Command to do something
pub struct Cmd {
/// This flag is specific to command
flag: bool,
arg: usize,
}
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
/// This flag is specific to the outer layer
flag: bool,
#[bpaf(external)]
cmd: Cmd,
}
Examples
In this example there’s only one command and it is required, so is the argument inside of it
% app cmd --arg 42
Options { flag: false, cmd: Cmd { flag: false, arg: 42 } }
If you don’t specify this command - parsing will fail
% app
Expected COMMAND ..., pass --help for usage information
You can have the same flag names inside and outside of the command, but it might be confusing for the end user. This example enables the outer flag
% app --flag cmd --arg 42
Options { flag: true, cmd: Cmd { flag: false, arg: 42 } }
And this one - both inside and outside
% app --flag cmd --arg 42 --flag
Options { flag: true, cmd: Cmd { flag: true, arg: 42 } }
And that’s the confusing part - unless you add context restrictions with
adjacent
and parse command first - outer flag wins.
So it’s best not to mix names on different levels
% app cmd --arg 42 --flag
Options { flag: true, cmd: Cmd { flag: false, arg: 42 } }
Commands show up on both outer level help
% app --help
Usage: [--flag] COMMAND ...
Available options:
--flag This flag is specific to the outer layer
-h, --help Prints help information
Available commands:
cmd Command to do something
As well as showing their own help
% app cmd --help
Command to do something
Usage: [--flag] --arg ARG
Available options:
--flag This flag is specific to command
--arg <ARG>
-h, --help Prints help information
Structs
argument
.command
positional