Struct bpaf::OptionParser
source · pub struct OptionParser<T> { /* private fields */ }
Expand description
Ready to run Parser
with additional information attached
Created with to_options
Implementations§
source§impl<T> OptionParser<T>
impl<T> OptionParser<T>
sourcepub fn run(self) -> Twhere
Self: Sized,
pub fn run(self) -> Twhere
Self: Sized,
Execute the OptionParser
, extract a parsed value or print some diagnostic and exit
Usage
/// Parses number of repetitions of `-v` on a command line
fn verbosity() -> OptionParser<usize> {
let parser = short('v')
.req_flag(())
.many()
.map(|xs|xs.len());
parser
.to_options()
.descr("Takes verbosity flag and does nothing else")
}
fn main() {
let verbosity: usize = verbosity().run();
}
Examples found in repository?
More examples
- examples/find.rs
- examples/env_logger.rs
- examples/dd.rs
- examples/xorg.rs
- examples/basic.rs
- examples/fallback_command.rs
- examples/positional_derive.rs
- examples/shared_args.rs
- src/lib.rs
- examples/enum_in_args.rs
- examples/at_least_two.rs
- examples/env_variable.rs
- examples/ex_positional.rs
- examples/top_to_bottom.rs
- examples/customize_help.rs
- examples/flatten.rs
- examples/enum_tuple.rs
- examples/negative.rs
- examples/cargo-cmd.rs
- examples/no_import.rs
- examples/multiple_fallback.rs
- examples/sensors.rs
- examples/cat.rs
- examples/rectangle.rs
- examples/git.rs
- examples/csample.rs
- examples/confusing.rs
- examples/verbose.rs
- examples/travel.rs
sourcepub fn try_run(self) -> Result<T, ParseFailure>where
Self: Sized,
pub fn try_run(self) -> Result<T, ParseFailure>where
Self: Sized,
Execute the OptionParser
, extract a parsed value or return a ParseFailure
In most cases using run
is sufficient, you can use try_run
if you
want to control the exit code or you need to perform a custom cleanup.
Usage
/// Parses number of repetitions of `-v` on a command line
fn verbosity() -> OptionParser<usize> {
let parser = short('v')
.req_flag(())
.many()
.map(|xs|xs.len());
parser
.to_options()
.descr("Takes verbosity flag and does nothing else")
}
fn main() {
let verbosity: Option<usize> = match verbosity().try_run() {
Ok(v) => Some(v),
Err(ParseFailure::Stdout(msg)) => {
print!("{}", msg); // completions are sad otherwise
None
}
Err(ParseFailure::Stderr(msg)) => {
eprintln!("{}", msg);
None
}
};
// Run cleanup tasks
}
Errors
ParseFailure
represents parsing errors, autocomplete results and generated --help
output.
Examples found in repository?
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
pub fn run(self) -> T
where
Self: Sized,
{
match self.try_run() {
Ok(t) => t,
Err(ParseFailure::Stdout(msg)) => {
print!("{}", msg); // completions are sad otherwise
std::process::exit(0);
}
Err(ParseFailure::Stderr(msg)) => {
eprintln!("{}", msg);
std::process::exit(1);
}
}
}
sourcepub fn run_inner(&self, args: Args) -> Result<T, ParseFailure>where
Self: Sized,
pub fn run_inner(&self, args: Args) -> Result<T, ParseFailure>where
Self: Sized,
Execute the OptionParser
and produce a values for unit tests or manual processing
#[test]
fn positional_argument() {
let parser =
positional::<String>("FILE")
.help("File to process")
.to_options();
let help = parser
.run_inner(Args::from(&["--help"]))
.unwrap_err()
.unwrap_stdout();
let expected_help = "\
Usage: <FILE>
Available positional items:
<FILE> File to process
Available options:
-h, --help Prints help information
";
assert_eq!(expected_help, help);
}
See also Args
and it’s From
impls to produce input and
ParseFailure::unwrap_stderr
/ ParseFailure::unwrap_stdout
for processing results.
Errors
If parser can’t produce desired result run_inner
returns ParseFailure
which represents runtime behavior: one branch to print something to stdout and exit with
success and the other branch to print something to stderr and exit with failure.
bpaf
generates contents of this ParseFailure
using expected textual output from
parse
, stdout/stderr isn’t actually captured.
Exact string reperentations may change between versions including minor releases.
Examples found in repository?
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
pub fn try_run(self) -> Result<T, ParseFailure>
where
Self: Sized,
{
let mut arg_vec = Vec::new();
#[cfg(feature = "autocomplete")]
let mut complete_vec = Vec::new();
let mut args = std::env::args_os();
#[allow(unused_variables)]
let name = args.next().expect("no command name from args_os?");
#[cfg(feature = "autocomplete")]
for arg in args {
if arg
.to_str()
.map_or(false, |s| s.starts_with("--bpaf-complete-"))
{
complete_vec.push(arg);
} else {
arg_vec.push(arg);
}
}
#[cfg(not(feature = "autocomplete"))]
arg_vec.extend(args);
#[cfg(feature = "autocomplete")]
let args = crate::complete_run::args_with_complete(name, &arg_vec, &complete_vec);
#[cfg(not(feature = "autocomplete"))]
let args = Args::from(arg_vec.as_slice());
self.run_inner(args)
}
More examples
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
pub(crate) fn args_with_complete(
os_name: OsString,
arguments: &[OsString],
complete_arguments: &[OsString],
) -> Args {
let path = PathBuf::from(os_name);
let path = path.file_name().expect("binary with no name?").to_str();
// not trying to run a completer - just make the arguments
if complete_arguments.is_empty() {
return Args::from(arguments);
}
let cargs = Args::from(complete_arguments);
match parse_comp_options().run_inner(cargs) {
Ok(comp) => {
let name = match path {
Some(path) => path,
None => panic!("app name is not utf8, giving up rendering completer"),
};
let rev = match comp {
CompOptions::Dump { style } => {
match style {
Style::Bash => dump_bash_completer(name),
Style::Zsh => dump_zsh_completer(name),
Style::Fish => dump_fish_completer(name),
Style::Elvish => dump_elvish_completer(name),
}
std::process::exit(0)
}
CompOptions::Complete { revision } => revision,
};
Args::from(arguments).set_comp(rev)
}
Err(err) => {
eprintln!("Can't parse bpaf complete options: {:?}", err);
std::process::exit(1);
}
}
}
sourcepub fn version(self, version: &'static str) -> Self
pub fn version(self, version: &'static str) -> Self
Set the version field.
By default bpaf
won’t include any version info and won’t accept --version
switch.
Combinatoric usage
use bpaf::*;
fn options() -> OptionParser<bool> {
short('s')
.switch()
.to_options()
.version(env!("CARGO_PKG_VERSION"))
}
Derive usage
version
annotation is available after options
and command
annotations, takes
an optional argument - version value to use, otherwise bpaf_derive
would use value from cargo.
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options, version)]
struct Options {
#[bpaf(short)]
switch: bool
}
Example
$ app --version
Version: 0.5.0
sourcepub fn descr(self, descr: &'static str) -> Self
pub fn descr(self, descr: &'static str) -> Self
Set the description field
Description field should be 1-2 lines long briefly explaining program purpose. If
description field is present bpaf
would print it right before the usage line.
Combinatoric usage
fn options() -> OptionParser<bool> {
short('s')
.switch()
.to_options()
.descr("This is a description")
.header("This is a header")
.footer("This is a footer")
}
Derive usage
bpaf_derive
uses doc comments on the struct
/ enum
to derive description, it skips single empty
lines and uses double empty lines break it into blocks. bpaf_derive
would use first block as the
description, second block - header, third block - footer.
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options, version)]
/// This is a description
///
///
/// This is a header
///
///
/// This is a footer
///
///
/// This is just a comment
struct Options {
#[bpaf(short)]
switch: bool
}
Example
This is a description
Usage: [-s]
This is a header
Available options:
-s
-h, --help Prints help information
-V, --version Prints version information
This is a footer
Examples found in repository?
6 7 8 9 10 11 12 13 14 15 16 17 18
fn main() {
let opt = short('d')
.help("Release the dragon")
.switch()
.to_options()
.descr("I am a program and I do things")
.header("Sometimes they even work.")
.footer("Beware `-d`, dragons be here")
.usage("You can call it with following flags: {usage}")
.run();
println!("{:?}", opt);
}
More examples
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);
}
5 6 7 8 9 10 11 12 13 14 15 16 17
fn main() {
let age = long("age").argument::<i64>("AGE");
let msg = "\
To pass a value that starts with a dash requres one one of two special syntaxes:
This will pass '-1' to '--age' handler and leave remaining arguments as is
--age=-1
This will transform everything after '--' into non flags, '--age' will handle '-1'
and positional handlers will be able to handle the rest.
--age -- -1";
let num = age.to_options().descr(msg).run();
println!("age: {}", num);
}
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
fn main() {
let file = positional::<OsString>("FILE")
.help("File name to concatenate, with no FILE or when FILE is -, read standard input")
.optional()
.parse::<_, Box<dyn Read>, std::io::Error>(|path| {
Ok(if let Some(path) = path {
if path == "-" {
Box::new(stdin())
} else {
Box::new(File::open(path)?)
}
} else {
Box::new(stdin())
})
})
.to_options()
.descr("Concatenate a file to standard output")
.run();
let reader = BufReader::new(file);
for line in reader.lines() {
println!("{}", line.unwrap());
}
}
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
fn main() {
let width = short('w')
.long("width")
.help("Width of the rectangle")
.argument::<usize>("PX");
let height = short('h')
.long("height")
.help("Height of the rectangle")
.argument::<usize>("PX");
let rect = construct!(Rect { width, height })
.group_help("Rectangle is defined by width and height in meters")
.optional();
let verbose = short('v')
.long("verbose")
.help("Print computation steps")
.switch();
let opt = construct!(Out { verbose, rect })
.to_options()
.descr("This program calculates rectangle's area")
.header("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
.footer("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^")
.run();
println!("{:#?}", opt);
}
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
fn main() {
let dry_run = long("dry_run").switch();
let all = long("all").switch();
let repository = positional::<String>("SRC").fallback("origin".to_string());
let fetch = construct!(Opt::Fetch {
dry_run,
all,
repository
})
.to_options()
.descr("fetches branches from remote repository");
let fetch_cmd = command("fetch", fetch);
let interactive = short('i').switch();
let all = long("all").switch();
let files = positional::<PathBuf>("FILE").many();
let add = construct!(Opt::Add {
interactive,
all,
files
})
.to_options()
.descr("add files to the staging area");
let add_cmd = command("add", add).help("add files to the staging area");
let opt = construct!([fetch_cmd, add_cmd])
.to_options()
.descr("The stupid content tracker")
.run();
println!("{:?}", opt);
}
sourcepub fn header(self, header: &'static str) -> Self
pub fn header(self, header: &'static str) -> Self
Set the header field
bpaf
displays the header between the usage line and a list of the available options in --help
output
Combinatoric usage
fn options() -> OptionParser<bool> {
short('s')
.switch()
.to_options()
.descr("This is a description")
.header("This is a header")
.footer("This is a footer")
}
Derive usage
bpaf_derive
uses doc comments on the struct
/ enum
to derive description, it skips single empty
lines and uses double empty lines break it into blocks. bpaf_derive
would use first block as the
description, second block - header, third block - footer.
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options, version)]
/// This is a description
///
///
/// This is a header
///
///
/// This is a footer
///
///
/// This is just a comment
struct Options {
#[bpaf(short)]
switch: bool
}
Example
This is a description
Usage: [-s]
This is a header
Available options:
-s
-h, --help Prints help information
-V, --version Prints version information
This is a footer
Examples found in repository?
6 7 8 9 10 11 12 13 14 15 16 17 18
fn main() {
let opt = short('d')
.help("Release the dragon")
.switch()
.to_options()
.descr("I am a program and I do things")
.header("Sometimes they even work.")
.footer("Beware `-d`, dragons be here")
.usage("You can call it with following flags: {usage}")
.run();
println!("{:?}", opt);
}
More examples
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
fn main() {
let width = short('w')
.long("width")
.help("Width of the rectangle")
.argument::<usize>("PX");
let height = short('h')
.long("height")
.help("Height of the rectangle")
.argument::<usize>("PX");
let rect = construct!(Rect { width, height })
.group_help("Rectangle is defined by width and height in meters")
.optional();
let verbose = short('v')
.long("verbose")
.help("Print computation steps")
.switch();
let opt = construct!(Out { verbose, rect })
.to_options()
.descr("This program calculates rectangle's area")
.header("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
.footer("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^")
.run();
println!("{:#?}", opt);
}
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
fn main() {
// Suppose we want to calcualte a total time of a travel, where parts of
// a travel can be given either as pairs of speed and distance or just by time.
// Speed can be given by KPH or MPH. Distance - either miles or km.
// parsers for speeds. Both speeds are converted to the same units
let mph = long("mph")
.help("speed in MPH")
.argument::<f64>("SPEED")
.map(|x| x * 1.6);
let kph = long("kph").help("Speed in KPH").argument::<f64>("SPEED");
// speed is either kph or mph, conversion to mph is handled by the parser
let speed = construct!([mph, kph]);
// parsers for distances, both are converted to the same units
let km = long("km").help("Distance in KM").argument::<f64>("KMs");
let mi = long("mi")
.help("distance in miles")
.argument::<f64>("MILES")
.map(|x| x * 1.6);
let dist = construct!([mi, km]);
// time, presumably in seconds
let time = long("time")
.help("Travel time in hours")
.argument::<f64>("TIME");
// parsed time is trivially converted to time segment
let segment_time = time.map(Segment::Time);
// parsed speed/distance is packed into SpeedDistance segment
let segment_speed = construct!(Segment::SpeedDistance { speed, dist });
// segment can be either of two defined
let segment = construct!([segment_speed, segment_time]);
// and we have several of them.
let parser = segment
.many()
.guard(|x| !x.is_empty(), "need at least one segment")
.guard(
|x| x.len() < 10,
"for more than 9 segments you need to purchase a premium subscription",
);
let descr = "Accepts one or more travel segments";
let header = "You need to specify one or more travel segments, segment is defined by
a pair of speed and distance or by time.
This example defines two separate travel segments, one given by speed/distance combo and one by time
travel --km 180 --kph 35 --time";
let decorated = parser.to_options().descr(descr).header(header);
// help message tries to explain what's needed:
// either --time OR one speed and one distance, both can be given in miles or km.
// number of speed flags must correspond to number of distance flags, more or
// less results in parser error messages
let opt = decorated.run();
println!("{:#?}", opt);
}
Set the footer field
bpaf
displays the footer after list of the available options in --help
output
Combinatoric usage
fn options() -> OptionParser<bool> {
short('s')
.switch()
.to_options()
.descr("This is a description")
.header("This is a header")
.footer("This is a footer")
}
Derive usage
bpaf_derive
uses doc comments on the struct
/ enum
to derive description, it skips single empty
lines and uses double empty lines break it into blocks. bpaf_derive
would use first block as the
description, second block - header, third block - footer.
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options, version)]
/// This is a description
///
///
/// This is a header
///
///
/// This is a footer
///
///
/// This is just a comment
struct Options {
#[bpaf(short)]
switch: bool
}
Example
This is a description
Usage: [-s]
This is a header
Available options:
-s
-h, --help Prints help information
-V, --version Prints version information
This is a footer
Examples found in repository?
6 7 8 9 10 11 12 13 14 15 16 17 18
fn main() {
let opt = short('d')
.help("Release the dragon")
.switch()
.to_options()
.descr("I am a program and I do things")
.header("Sometimes they even work.")
.footer("Beware `-d`, dragons be here")
.usage("You can call it with following flags: {usage}")
.run();
println!("{:?}", opt);
}
More examples
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
fn main() {
let width = short('w')
.long("width")
.help("Width of the rectangle")
.argument::<usize>("PX");
let height = short('h')
.long("height")
.help("Height of the rectangle")
.argument::<usize>("PX");
let rect = construct!(Rect { width, height })
.group_help("Rectangle is defined by width and height in meters")
.optional();
let verbose = short('v')
.long("verbose")
.help("Print computation steps")
.switch();
let opt = construct!(Out { verbose, rect })
.to_options()
.descr("This program calculates rectangle's area")
.header("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
.footer("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^")
.run();
println!("{:#?}", opt);
}
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());
}
sourcepub fn usage(self, usage: &'static str) -> Self
pub fn usage(self, usage: &'static str) -> Self
Set custom usage field
Custom usage field to use instead of one derived by bpaf
. Custom message should contain
"Usage: "
prefix if you want to display one.
Before using it bpaf
would replace "{usage}"
tokens inside a custom usage string with
automatically generated usage.
Combinatoric usage
fn options() -> OptionParser<bool> {
short('s')
.switch()
.to_options()
.usage("Usage: my_program: {usage}")
}
Derive usage
Not available directly, but you can call usage
on generated OptionParser
.
Examples found in repository?
6 7 8 9 10 11 12 13 14 15 16 17 18
fn main() {
let opt = short('d')
.help("Release the dragon")
.switch()
.to_options()
.descr("I am a program and I do things")
.header("Sometimes they even work.")
.footer("Beware `-d`, dragons be here")
.usage("You can call it with following flags: {usage}")
.run();
println!("{:?}", opt);
}
sourcepub fn command(self, name: &'static str) -> ParseCommand<T>where
T: 'static,
pub fn command(self, name: &'static str) -> ParseCommand<T>where
T: 'static,
Turn OptionParser
into subcommand parser
This is identical to command
sourcepub fn check_invariants(&self, _cosmetic: bool)
pub fn check_invariants(&self, _cosmetic: bool)
Check the invariants bpaf
relies on for normal operations
Takes a parameter whether to check for cosmetic invariants or not (max help width exceeding 120 symbols, etc), currently not in use
Best used as part of your test suite:
#[test]
fn check_options() {
options().check_invariants(false)
}
Panics
check_invariants
indicates problems with panic