Expand description
Take next unconsumed item on the command line as raw String
or OsString
any
is designed to consume items that don’t fit into usual flag
/switch
/positional
/argument
/command
classification
any
behaves similar to positional
so you should be using it near the right most end of
the consumer struct. Note, consuming “anything” also consumes --help
unless restricted
with guard
. It’s better stick to positional
unless you are trying to consume raw options
to pass to some other process or do some special handling.
When using combinatoring 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 hands. For any
you would usually consume it either as a String
or OsString
.
fn parse_any() -> impl Parser<OsString> {
any::<OsString>("ANYTHING")
}
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
See adjacent
for more examples
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()
}