pub fn literal(val: &'static str) -> ParseAny<()>
Expand description
A specialized version of any
that consumes an arbitrary string
By default literal
behaves similarly to positional
so you should be using it near the
rightmost end of the consumer struct and it will only try to parse the first unconsumed
item on the command line. It is possible to lift this restriction by calling
anywhere
on the parser.
Combinatoric example
#[derive(Debug, Clone)]
pub struct Options {
block_size: usize,
count: usize,
output_file: String,
turbo: bool,
}
/// Parses a string that starts with `name`, returns the suffix parsed in a usual way
fn tag<T>(name: &'static str, meta: &str, help: impl Into<Doc>) -> impl Parser<T>
where
T: FromStr,
<T as FromStr>::Err: std::fmt::Display,
{
// closure inside checks if command line argument starts with a given name
// and if it is - it accepts it, otherwise it behaves like it never saw it
// it is possible to parse OsString here and strip the prefix with
// `os_str_bytes` or a similar crate
any("", move |s: String| Some(s.strip_prefix(name)?.to_owned()))
// this defines custom metavar for the help message
// so it looks like something it designed to parse
.metavar(&[(name, Style::Literal), (meta, Style::Metavar)][..])
.help(help)
// this makes it so tag parser tries to read all (unconsumed by earlier parsers)
// item on a command line instead of trying and failing on the first one
.anywhere()
// At this point parser produces `String` while consumer might expect some other
// type. [`parse`](Parser::parse) handles that
.parse(|s| s.parse())
}
pub fn options() -> OptionParser<Options> {
let block_size = tag("bs=", "BLOCK", "How many bytes to read at once")
.fallback(1024)
.display_fallback();
let count = tag("count=", "NUM", "How many blocks to read").fallback(1);
let output_file = tag("of=", "FILE", "Save results into this file");
// this consumes literal value of "+turbo" locate and produces `bool`
let turbo = literal("+turbo")
.help("Engage turbo mode!")
.anywhere()
.map(|_| true)
.fallback(false);
construct!(Options {
block_size,
count,
output_file,
turbo
})
.to_options()
}
fn main() {
println!("{:?}", options().run())
}
Derive example
// This example is still technically derive API, but derive is limited to gluing
// things together and keeping macro complexity under control.
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
// `external` here and below derives name from the field name, looking for
// functions called `block_size`, `count`, etc that produce parsers of
// the right type.
// A different way would be to write down the name explicitly:
// #[bpaf(external(block_size), fallback(1024), display_fallback)]
#[bpaf(external, fallback(1024), display_fallback)]
block_size: usize,
#[bpaf(external, fallback(1))]
count: usize,
#[bpaf(external)]
output_file: String,
#[bpaf(external)]
turbo: bool,
}
fn block_size() -> impl Parser<usize> {
tag("bs=", "BLOCK", "How many bytes to read at once")
}
fn count() -> impl Parser<usize> {
tag("count=", "NUM", "How many blocks to read")
}
fn output_file() -> impl Parser<String> {
tag("of=", "FILE", "Save results into this file")
}
fn turbo() -> impl Parser<bool> {
literal("+turbo")
.help("Engage turbo mode!")
.anywhere()
.map(|_| true)
.fallback(false)
}
/// Parses a string that starts with `name`, returns the suffix parsed in a usual way
fn tag<T>(name: &'static str, meta: &str, help: impl Into<Doc>) -> impl Parser<T>
where
T: FromStr,
<T as FromStr>::Err: std::fmt::Display,
{
// closure inside checks if command line argument starts with a given name
// and if it is - it accepts it, otherwise it behaves like it never saw it
// it is possible to parse OsString here and strip the prefix with
// `os_str_bytes` or a similar crate
any("", move |s: String| Some(s.strip_prefix(name)?.to_owned()))
// this defines custom metavar for the help message
// so it looks like something it designed to parse
.metavar(&[(name, Style::Literal), (meta, Style::Metavar)][..])
.help(help)
// this makes it so tag parser tries to read all (unconsumed by earlier parsers)
// item on a command line instead of trying and failing on the first one
.anywhere()
// At this point parser produces `String` while consumer might expect some other
// type. [`parse`](Parser::parse) handles that
.parse(|s| s.parse())
}
fn main() {
println!("{:?}", options().run())
}
Output
Instead of usual metavariable any
parsers take something that can represent any value
$ app --help
Usage: app [bs=BLOCK] [count=NUM] of=FILE [+turbo]
Available options:
- bs=BLOCK
- How many bytes to read at once
- [default: 1024]
- count=NUM
- How many blocks to read
- of=FILE
- Save results into this file
- +turbo
- Engage turbo mode!
- -h, --help
- Prints help information
Output file is required in this parser, other values are optional
$ app
Error: expected of=FILE, pass --help for usage information
Error: expected of=FILE, pass --help for usage information
$ app of=simple.txt
Options { block_size: 1024, count: 1, output_file: "simple.txt", turbo: false }
Options { block_size: 1024, count: 1, output_file: "simple.txt", turbo: false }
Since options are defined with anywhere
- order doesn’t matter
$ app bs=10 of=output.rs +turbo
Options { block_size: 10, count: 1, output_file: "output.rs", turbo: true }
Options { block_size: 10, count: 1, output_file: "output.rs", turbo: true }
$ app +turbo bs=10 of=output.rs
Options { block_size: 10, count: 1, output_file: "output.rs", turbo: true }
Options { block_size: 10, count: 1, output_file: "output.rs", turbo: true }
$ app bs=65536 count=12 of=hello_world.rs
Options { block_size: 65536, count: 12, output_file: "hello_world.rs", turbo: false }
Options { block_size: 65536, count: 12, output_file: "hello_world.rs", turbo: false }
§See also
any
- a generic version of literal
that uses function to decide if value is to be parsed
or not.
Examples found in repository?
examples/find.rs (line 33)
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
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()
}
More examples
examples/cargo-cmd.rs (line 22)
16 17 18 19 20 21 22 23 24 25 26 27 28
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 = literal("cmd").optional().hide();
let combined_parser = construct!(cmd, parser).map(|x| x.1);
let opts = combined_parser.to_options().run();
println!("{:?}", opts);
}