Function bpaf::positional
source · pub fn positional<T>(metavar: &'static str) -> ParsePositional<T>
Expand description
Parse a positional argument
For named flags and arguments ordering generally doesn’t matter: most programs would
understand -O2 -v
the same way as -v -O2
, but for positional items order matters: in *nix
cat hello world
and cat world hello
would display contents of the same two files but in
a different order.
When using combinatoric API you can specify the type with turbofish, for parsing types
that don’t implement FromStr
you can use consume a String
/OsString
first and parse
it by hand.
fn parse_pos() -> impl Parser<usize> {
positional::<usize>("POS")
}
§Important restriction
To parse positional arguments from a command line you should place parsers for all your
named values before parsers for positional items and commands. In derive API fields parsed as
positional items or commands should be at the end of your struct
/enum
. The same rule applies
to parsers with positional fields or commands inside: such parsers should go to the end as well.
Use check_invariants
in your test to ensure correctness.
For example for non-positional non_pos
and positional pos
parsers
let valid = construct!(non_pos(), pos());
let invalid = construct!(pos(), non_pos());
bpaf
panics during help generation unless this restriction holds
Without using --
bpaf
would only accept items that don’t start with -
as positional, you
can use any
to work around this restriction.
By default bpaf
accepts positional items with or without --
where values permit, you can
further restrict the parser to accept positional items only on the right side of --
using
strict
.
Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
verbose: bool,
crate_name: String,
feature_name: Option<String>,
}
pub fn options() -> OptionParser<Options> {
let verbose = short('v')
.long("verbose")
.help("Display detailed information")
.switch();
let crate_name = positional("CRATE").help("Crate name to use");
let feature_name = positional("FEATURE")
.help("Display information about this feature")
.optional();
construct!(Options {
verbose,
// You must place positional items and commands after
// all other parsers
crate_name,
feature_name
})
.to_options()
}
fn main() {
println!("{:?}", options().run())
}
Derive example
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
/// Display detailed information
#[bpaf(short, long)]
verbose: bool,
// You must place positional items and commands after
// all other parsers
#[bpaf(positional("CRATE"))]
/// Crate name to use
crate_name: String,
#[bpaf(positional("FEATURE"))]
/// Display information about this feature
feature_name: Option<String>,
}
fn main() {
println!("{:?}", options().run())
}
Output
Positional items show up in a separate group of arguments if they contain a help message, otherwise they will show up only in Usage part.
Usage: app [-v] CRATE [FEATURE]
- CRATE
- Crate name to use
- FEATURE
- Display information about this feature
- -v, --verbose
- Display detailed information
- -h, --help
- Prints help information
You can mix positional items with regular items
Options { verbose: true, crate_name: "bpaf", feature_name: None }
And since bpaf
API expects to have non positional items consumed before positional ones - you
can use them in a different order. In this example bpaf
corresponds to a crate_name
field and
--verbose
– to verbose
.
Options { verbose: true, crate_name: "bpaf", feature_name: None }
In previous examples optional field feature
was missing, this one contains it.
Options { verbose: false, crate_name: "bpaf", feature_name: Some("autocomplete") }
Users can use --
to tell bpaf
to treat remaining items as positionals - this might be
required to handle unusual items.
Options { verbose: false, crate_name: "bpaf", feature_name: Some("--verbose") }
Options { verbose: false, crate_name: "bpaf", feature_name: Some("--verbose") }
Without using --
bpaf
would only accept items that don’t start with -
as positional.
Error: expected CRATE, got --detailed. Pass --help for usage information
Error: expected CRATE, pass --help for usage information
You can use any
to work around this restriction.
Examples found in repository?
More examples
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 94 95 96 97 98 99 100 101 102 103 104 105 106 107
fn user() -> impl Parser<Option<String>> {
// match only literal "-user"
let tag = literal("-user").anywhere();
let value = positional("USER").help("User name");
construct!(tag, value)
.adjacent()
.map(|pair| pair.1)
.optional()
}
// parsers -exec xxx yyy zzz ;
fn exec() -> impl Parser<Option<Vec<OsString>>> {
let tag = literal("-exec")
.help("for every file find finds execute a separate shell command")
.anywhere();
let item = any::<OsString, _, _>("ITEM", |s| (s != ";").then_some(s))
.help("command with its arguments, find will replace {} with a file name")
.many();
let endtag = any::<String, _, _>(";", |s| (s == ";").then_some(()))
.help("anything after literal \";\" will be considered a regular option again");
construct!(tag, item, endtag)
.adjacent()
.map(|triple| triple.1)
.optional()
}
/// 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 = literal("-mode").anywhere();
// `any` here is used to parse an arbitrary string that can also start with dash (-)
// regular positional parser won't work here
let mode = any("MODE", Some)
.help("(perm | -perm | /perm), where perm is any subset of rwx characters, ex +rw")
.parse::<_, _, String>(|s: String| {
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)?))
}
});
construct!(tag, mode)
.adjacent()
.map(|pair| pair.1)
.optional()
}
pub fn options() -> OptionParser<Options> {
let paths = positional::<PathBuf>("PATH").many();
construct!(Options {
exec(),
user(),
perm(),
paths,
})
.to_options()
}
28 29 30 31 32 33 34 35 36 37 38 39
fn extension() -> impl Parser<(String, bool)> {
let state = any("(+|-)ext", |s: String| match s.as_str() {
"-ext" => Some(false),
"+ext" => Some(true),
_ => None,
})
.anywhere();
let name = positional::<String>("EXT")
.help("Extension to enable or disable, see documentation for the full list");
construct!(state, name).adjacent().map(|(a, b)| (b, a))
}