docs(example): Update for value_parser

This commit is contained in:
Ed Page 2022-05-23 10:50:11 -05:00
parent 9a913c40e1
commit 33e94df212
23 changed files with 254 additions and 119 deletions

View file

@ -37,9 +37,9 @@ OPTIONS:
Then to directly invoke the command, run:
```console
$ cargo-example example
None
Ok(None)
$ cargo-example example --manifest-path Cargo.toml
Some("Cargo.toml")
Ok(Some("Cargo.toml"))
```

View file

@ -8,7 +8,7 @@ fn main() {
clap::command!("example").arg(
clap::arg!(--"manifest-path" <PATH>)
.required(false)
.allow_invalid_utf8(true),
.value_parser(clap::value_parser!(std::path::PathBuf)),
),
);
let matches = cmd.get_matches();
@ -16,8 +16,6 @@ fn main() {
Some(("example", matches)) => matches,
_ => unreachable!("clap should ensure we don't get here"),
};
let manifest_path = matches
.value_of_os("manifest-path")
.map(std::path::PathBuf::from);
let manifest_path = matches.get_one::<std::path::PathBuf>("manifest-path");
println!("{:?}", manifest_path);
}

View file

@ -274,16 +274,16 @@ Notes:
- Implies `arg.takes_value(true).allow_invalid_utf8(true)`
- `from_occurrences`:
- Implies `arg.takes_value(false).multiple_occurrences(true)`
- Reads from `clap::ArgMatches::occurrences_of` rather than a `value_of` function
- Reads from `clap::ArgMatches::occurrences_of` rather than a `get_one` function
- Note: operations on values, like `default_value`, are unlikely to do what you want
- `from_flag`
- Implies `arg.takes_value(false)`
- Reads from `clap::ArgMatches::is_present` rather than a `value_of` function
- Reads from `clap::ArgMatches::is_present` rather than a `get_one` function
- Note: operations on values, like `default_value`, are unlikely to do what you want
**Warning:**
- To support non-UTF8 paths, you must use `parse(from_os_str)`, otherwise
`clap` will use `clap::ArgMatches::value_of` with `PathBuf::FromStr`.
- To support non-UTF8 paths, you should use `#[clap(value_parser)]` otherwise
`clap` will parse it as a `String` which will fail on some paths.
## Doc Comments

View file

@ -1,26 +1,44 @@
// Note: this requires the `cargo` feature
use clap::{arg, command};
use clap::{arg, command, value_parser};
fn main() {
let matches = command!()
.arg(arg!(eff: -f))
.arg(arg!(pea: -p <PEAR>).required(false))
.arg(
arg!(slop: [SLOP]).multiple_occurrences(true).last(true), // Indicates that `slop` is only accessible after `--`.
arg!(pea: -p <PEAR>)
.required(false)
.value_parser(value_parser!(String)),
)
.arg(
// Indicates that `slop` is only accessible after `--`.
arg!(slop: [SLOP])
.multiple_occurrences(true)
.last(true)
.value_parser(value_parser!(String)),
)
.get_matches();
// This is what will happen with `myprog -f -p=bob -- sloppy slop slop`...
println!("-f used: {:?}", matches.is_present("eff")); // -f used: true
println!("-p's value: {:?}", matches.value_of("pea")); // -p's value: Some("bob")
// -f used: true
println!("-f used: {:?}", matches.is_present("eff"));
// -p's value: Some("bob")
println!(
"-p's value: {:?}",
matches
.get_one::<String>("pea")
.expect("matches definition")
);
// 'slops' values: Some(["sloppy", "slop", "slop"])
println!(
"'slops' values: {:?}",
matches
.values_of("slop")
.get_many::<String>("slop")
.expect("matches definition")
.map(|vals| vals.collect::<Vec<_>>())
.unwrap_or_default()
); // 'slops' values: Some(["sloppy", "slop", "slop"])
);
// Continued program logic goes here...
}

View file

@ -1,5 +1,6 @@
// Note: this requires the `cargo` feature
use std::ffi::OsString;
use std::path::PathBuf;
use clap::{arg, Command};
@ -27,7 +28,7 @@ fn cli() -> Command<'static> {
Command::new("add")
.about("adds things")
.arg_required_else_help(true)
.arg(arg!(<PATH> ... "Stuff to add").allow_invalid_utf8(true)),
.arg(arg!(<PATH> ... "Stuff to add").value_parser(clap::value_parser!(PathBuf))),
)
.subcommand(
Command::new("stash")
@ -50,20 +51,27 @@ fn main() {
Some(("clone", sub_matches)) => {
println!(
"Cloning {}",
sub_matches.value_of("REMOTE").expect("required")
sub_matches
.get_one::<String>("REMOTE")
.expect("matches definition")
.expect("required")
);
}
Some(("push", sub_matches)) => {
println!(
"Pushing to {}",
sub_matches.value_of("REMOTE").expect("required")
sub_matches
.get_one::<String>("REMOTE")
.expect("matches definition")
.expect("required")
);
}
Some(("add", sub_matches)) => {
let paths = sub_matches
.values_of_os("PATH")
.unwrap_or_default()
.map(PathBuf::from)
.get_many::<PathBuf>("PATH")
.expect("matches definition")
.into_iter()
.flatten()
.collect::<Vec<_>>();
println!("Adding {:?}", paths);
}
@ -71,15 +79,21 @@ fn main() {
let stash_command = sub_matches.subcommand().unwrap_or(("push", sub_matches));
match stash_command {
("apply", sub_matches) => {
let stash = sub_matches.value_of("STASH");
let stash = sub_matches
.get_one::<String>("STASH")
.expect("matches definition");
println!("Applying {:?}", stash);
}
("pop", sub_matches) => {
let stash = sub_matches.value_of("STASH");
let stash = sub_matches
.get_one::<String>("STASH")
.expect("matches definition");
println!("Popping {:?}", stash);
}
("push", sub_matches) => {
let message = sub_matches.value_of("message");
let message = sub_matches
.get_one::<String>("message")
.expect("matches definition");
println!("Pushing {:?}", message);
}
(name, _) => {
@ -89,8 +103,10 @@ fn main() {
}
Some((ext, sub_matches)) => {
let args = sub_matches
.values_of_os("")
.unwrap_or_default()
.get_many::<OsString>("")
.expect("matches definition")
.into_iter()
.flatten()
.collect::<Vec<_>>();
println!("Calling out to {:?} with {:?}", ext, args);
}

View file

@ -1,6 +1,7 @@
use std::path::PathBuf;
use std::process::exit;
use clap::{Arg, Command};
use clap::{value_parser, Arg, Command};
fn applet_commands() -> [Command<'static>; 2] {
[
@ -24,6 +25,7 @@ fn main() {
.exclusive(true)
.takes_value(true)
.default_missing_value("/usr/local/bin")
.value_parser(value_parser!(PathBuf))
.use_value_delimiter(false),
)
.subcommands(applet_commands()),

View file

@ -71,13 +71,23 @@ fn main() {
match matches.subcommand() {
Some(("sync", sync_matches)) => {
if sync_matches.is_present("search") {
let packages: Vec<_> = sync_matches.values_of("search").unwrap().collect();
let packages: Vec<_> = sync_matches
.get_many::<String>("search")
.expect("matches definition")
.expect("is present")
.map(|s| s.as_str())
.collect();
let values = packages.join(", ");
println!("Searching for {}...", values);
return;
}
let packages: Vec<_> = sync_matches.values_of("package").unwrap().collect();
let packages: Vec<_> = sync_matches
.get_many::<String>("package")
.expect("matches definition")
.expect("is present")
.map(|s| s.as_str())
.collect();
let values = packages.join(", ");
if sync_matches.is_present("info") {
@ -87,11 +97,17 @@ fn main() {
}
}
Some(("query", query_matches)) => {
if let Some(packages) = query_matches.values_of("info") {
let comma_sep = packages.collect::<Vec<_>>().join(", ");
if let Some(packages) = query_matches
.get_many::<String>("info")
.expect("matches definition")
{
let comma_sep = packages.map(|s| s.as_str()).collect::<Vec<_>>().join(", ");
println!("Retrieving info for {}...", comma_sep);
} else if let Some(queries) = query_matches.values_of("search") {
let comma_sep = queries.collect::<Vec<_>>().join(", ");
} else if let Some(queries) = query_matches
.get_many::<String>("search")
.expect("matches definition")
{
let comma_sep = queries.map(|s| s.as_str()).collect::<Vec<_>>().join(", ");
println!("Searching Locally for {}...", comma_sep);
} else {
println!("Displaying all locally installed packages...");

View file

@ -1,7 +1,8 @@
// Note: this requires the `cargo` feature
use clap::{arg, command, Command};
use std::path::Path;
use std::path::PathBuf;
use clap::{arg, command, value_parser, Command};
fn main() {
let matches = command!()
@ -12,8 +13,7 @@ fn main() {
)
// We don't have syntax yet for optional options, so manually calling `required`
.required(false)
// Support non-UTF8 paths
.allow_invalid_utf8(true),
.value_parser(value_parser!(PathBuf)),
)
.arg(arg!(
-d --debug ... "Turn debugging information on"
@ -26,12 +26,17 @@ fn main() {
.get_matches();
// You can check the value provided by positional arguments, or option arguments
if let Some(name) = matches.value_of("name") {
if let Some(name) = matches
.get_one::<String>("name")
.expect("matches definition")
{
println!("Value for name: {}", name);
}
if let Some(raw_config) = matches.value_of_os("config") {
let config_path = Path::new(raw_config);
if let Some(config_path) = matches
.get_one::<PathBuf>("config")
.expect("matches definition")
{
println!("Value for config: {}", config_path.display());
}

View file

@ -11,6 +11,18 @@ fn main() {
.arg(arg!(--one <VALUE>))
.get_matches();
println!("two: {:?}", matches.value_of("two").expect("required"));
println!("one: {:?}", matches.value_of("one").expect("required"));
println!(
"two: {:?}",
matches
.get_one::<String>("two")
.expect("matches definition")
.expect("required")
);
println!(
"one: {:?}",
matches
.get_one::<String>("one")
.expect("matches definition")
.expect("required")
);
}

View file

@ -9,6 +9,18 @@ fn main() {
.arg(arg!(--one <VALUE>))
.get_matches();
println!("two: {:?}", matches.value_of("two").expect("required"));
println!("one: {:?}", matches.value_of("one").expect("required"));
println!(
"two: {:?}",
matches
.get_one::<String>("two")
.expect("matches definition")
.expect("required")
);
println!(
"one: {:?}",
matches
.get_one::<String>("one")
.expect("matches definition")
.expect("required")
);
}

View file

@ -8,6 +8,18 @@ fn main() {
.arg(arg!(--one <VALUE>))
.get_matches();
println!("two: {:?}", matches.value_of("two").expect("required"));
println!("one: {:?}", matches.value_of("one").expect("required"));
println!(
"two: {:?}",
matches
.get_one::<String>("two")
.expect("matches definition")
.expect("required")
);
println!(
"one: {:?}",
matches
.get_one::<String>("one")
.expect("matches definition")
.expect("required")
);
}

View file

@ -7,5 +7,10 @@ fn main() {
.arg(arg!(-n --name <NAME>).required(false))
.get_matches();
println!("name: {:?}", matches.value_of("name"));
println!(
"name: {:?}",
matches
.get_one::<String>("name")
.expect("matches definition")
);
}

View file

@ -5,5 +5,10 @@ use clap::{arg, command};
fn main() {
let matches = command!().arg(arg!([NAME])).get_matches();
println!("NAME: {:?}", matches.value_of("NAME"));
println!(
"NAME: {:?}",
matches
.get_one::<String>("NAME")
.expect("matches definition")
);
}

View file

@ -17,7 +17,9 @@ fn main() {
match matches.subcommand() {
Some(("add", sub_matches)) => println!(
"'myapp add' was used, name is: {:?}",
sub_matches.value_of("NAME")
sub_matches
.get_one::<String>("NAME")
.expect("matches definition")
),
_ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"),
}

View file

@ -10,7 +10,8 @@ fn main() {
println!(
"NAME: {:?}",
matches
.value_of("NAME")
.get_one::<String>("NAME")
.expect("matches definition")
.expect("default ensures there is always a value")
);
}

View file

@ -1,6 +1,6 @@
// Note: this requires the `cargo` feature
use clap::{arg, command, ArgEnum, PossibleValue};
use clap::{arg, command, value_parser, ArgEnum};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ArgEnum)]
enum Mode {
@ -8,48 +8,19 @@ enum Mode {
Slow,
}
impl Mode {
pub fn possible_values() -> impl Iterator<Item = PossibleValue<'static>> {
Mode::value_variants()
.iter()
.filter_map(ArgEnum::to_possible_value)
}
}
impl std::fmt::Display for Mode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.to_possible_value()
.expect("no values are skipped")
.get_name()
.fmt(f)
}
}
impl std::str::FromStr for Mode {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
for variant in Self::value_variants() {
if variant.to_possible_value().unwrap().matches(s, false) {
return Ok(*variant);
}
}
Err(format!("Invalid variant: {}", s))
}
}
fn main() {
let matches = command!()
.arg(
arg!(<MODE>)
.help("What mode to run the program in")
.possible_values(Mode::possible_values()),
.value_parser(value_parser!(Mode)),
)
.get_matches();
// Note, it's safe to call unwrap() because the arg is required
match matches
.value_of_t("MODE")
.get_one::<Mode>("MODE")
.expect("matches definition")
.expect("'MODE' is required and parsing will fail if its missing")
{
Mode::Fast => {

View file

@ -7,14 +7,16 @@ fn main() {
.arg(
arg!(<MODE>)
.help("What mode to run the program in")
.possible_values(["fast", "slow"]),
.value_parser(["fast", "slow"]),
)
.get_matches();
// Note, it's safe to call unwrap() because the arg is required
match matches
.value_of("MODE")
.get_one::<String>("MODE")
.expect("matches definition")
.expect("'MODE' is required and parsing will fail if its missing")
.as_str()
{
"fast" => {
println!("Hare");

View file

@ -1,19 +1,20 @@
// Note: this requires the `cargo` feature
use clap::{arg, command};
use clap::{arg, command, value_parser};
fn main() {
let matches = command!()
.arg(
arg!(<PORT>)
.help("Network port to use")
.validator(|s| s.parse::<usize>()),
.value_parser(value_parser!(u16).range(1..)),
)
.get_matches();
// Note, it's safe to call unwrap() because the arg is required
let port: usize = matches
.value_of_t("PORT")
let port: u16 = *matches
.get_one::<u16>("PORT")
.expect("matches definition")
.expect("'PORT' is required and parsing will fail if its missing");
println!("PORT = {}", port);
}

View file

@ -9,25 +9,26 @@ fn main() {
.arg(
arg!(<PORT>)
.help("Network port to use")
.validator(port_in_range),
.value_parser(port_in_range),
)
.get_matches();
// Note, it's safe to call unwrap() because the arg is required
let port: usize = matches
.value_of_t("PORT")
let port: u16 = *matches
.get_one::<u16>("PORT")
.expect("matches definition")
.expect("'PORT' is required and parsing will fail if its missing");
println!("PORT = {}", port);
}
const PORT_RANGE: RangeInclusive<usize> = 1..=65535;
fn port_in_range(s: &str) -> Result<(), String> {
fn port_in_range(s: &str) -> Result<u16, String> {
let port: usize = s
.parse()
.map_err(|_| format!("`{}` isn't a port number", s))?;
if PORT_RANGE.contains(&port) {
Ok(())
Ok(port as u16)
} else {
Err(format!(
"Port not in range {}-{}",

View file

@ -1,6 +1,8 @@
// Note: this requires the `cargo` feature
use clap::{arg, command, ArgGroup};
use std::path::PathBuf;
use clap::{arg, command, value_parser, ArgGroup};
fn main() {
// Create application like normal
@ -18,15 +20,25 @@ fn main() {
)
// Arguments can also be added to a group individually, these two arguments
// are part of the "input" group which is not required
.arg(arg!([INPUT_FILE] "some regular input").group("input"))
.arg(
arg!([INPUT_FILE] "some regular input")
.value_parser(value_parser!(PathBuf))
.group("input"),
)
.arg(
arg!(--"spec-in" <SPEC_IN> "some special input argument")
.required(false)
.value_parser(value_parser!(PathBuf))
.group("input"),
)
// Now let's assume we have a -c [config] argument which requires one of
// (but **not** both) the "input" arguments
.arg(arg!(config: -c <CONFIG>).required(false).requires("input"))
.arg(
arg!(config: -c <CONFIG>)
.required(false)
.value_parser(value_parser!(PathBuf))
.requires("input"),
)
.get_matches();
// Let's assume the old version 1.2.3
@ -35,8 +47,11 @@ fn main() {
let mut patch = 3;
// See if --set-ver was used to set the version manually
let version = if let Some(ver) = matches.value_of("set-ver") {
ver.to_string()
let version = if let Some(ver) = matches
.get_one::<String>("set-ver")
.expect("matches definition")
{
ver.to_owned()
} else {
// Increment the one requested (in a real program, we'd reset the lower numbers)
let (maj, min, pat) = (
@ -58,12 +73,23 @@ fn main() {
// Check for usage of -c
if matches.is_present("config") {
let input = matches
.value_of("INPUT_FILE")
.unwrap_or_else(|| matches.value_of("spec-in").unwrap());
.get_one::<PathBuf>("INPUT_FILE")
.expect("matches definition")
.unwrap_or_else(|| {
matches
.get_one::<PathBuf>("spec-in")
.expect("matches definition")
.unwrap()
})
.display();
println!(
"Doing work using input {} and config {}",
input,
matches.value_of("config").unwrap()
matches
.get_one::<PathBuf>("config")
.expect("matches definition")
.unwrap()
.display()
);
}
}

View file

@ -1,6 +1,8 @@
// Note: this requires the `cargo` feature
use clap::{arg, command, ErrorKind};
use std::path::PathBuf;
use clap::{arg, command, value_parser, ErrorKind};
fn main() {
// Create application like normal
@ -12,11 +14,19 @@ fn main() {
.arg(arg!(--patch "auto inc patch"))
// Arguments can also be added to a group individually, these two arguments
// are part of the "input" group which is not required
.arg(arg!([INPUT_FILE] "some regular input"))
.arg(arg!(--"spec-in" <SPEC_IN> "some special input argument").required(false))
.arg(arg!([INPUT_FILE] "some regular input").value_parser(value_parser!(PathBuf)))
.arg(
arg!(--"spec-in" <SPEC_IN> "some special input argument")
.required(false)
.value_parser(value_parser!(PathBuf)),
)
// Now let's assume we have a -c [config] argument which requires one of
// (but **not** both) the "input" arguments
.arg(arg!(config: -c <CONFIG>).required(false));
.arg(
arg!(config: -c <CONFIG>)
.required(false)
.value_parser(value_parser!(PathBuf)),
);
let matches = cmd.get_matches_mut();
// Let's assume the old version 1.2.3
@ -25,7 +35,10 @@ fn main() {
let mut patch = 3;
// See if --set-ver was used to set the version manually
let version = if let Some(ver) = matches.value_of("set-ver") {
let version = if let Some(ver) = matches
.get_one::<String>("set-ver")
.expect("matches definition")
{
if matches.is_present("major") || matches.is_present("minor") || matches.is_present("patch")
{
cmd.error(
@ -62,19 +75,29 @@ fn main() {
// Check for usage of -c
if matches.is_present("config") {
let input = matches
.value_of("INPUT_FILE")
.or_else(|| matches.value_of("spec-in"))
.get_one::<PathBuf>("INPUT_FILE")
.expect("matches definition")
.or_else(|| {
matches
.get_one::<PathBuf>("spec-in")
.expect("matches definition")
})
.unwrap_or_else(|| {
cmd.error(
ErrorKind::MissingRequiredArgument,
"INPUT_FILE or --spec-in is required when using --config",
)
.exit()
});
})
.display();
println!(
"Doing work using input {} and config {}",
input,
matches.value_of("config").unwrap()
matches
.get_one::<PathBuf>("config")
.expect("matches definition")
.unwrap()
.display()
);
}
}

View file

@ -1,13 +1,14 @@
// Note: this requires the `cargo` feature
use clap::{arg, command};
use clap::{arg, command, value_parser};
fn main() {
let matches = cmd().get_matches();
// Note, it's safe to call unwrap() because the arg is required
let port: usize = matches
.value_of_t("PORT")
let port: usize = *matches
.get_one::<usize>("PORT")
.expect("matches definition")
.expect("'PORT' is required and parsing will fail if its missing");
println!("PORT = {}", port);
}
@ -16,7 +17,7 @@ fn cmd() -> clap::Command<'static> {
command!().arg(
arg!(<PORT>)
.help("Network port to use")
.validator(|s| s.parse::<usize>()),
.value_parser(value_parser!(usize)),
)
}

View file

@ -444,7 +444,7 @@ For more information try --help
### Validated values
More generally, you can parse into any data type.
More generally, you can validate and parse into any data type.
[Example:](04_02_parse.rs)
```console
@ -471,9 +471,15 @@ error: Invalid value "foobar" for '<PORT>': invalid digit found in string
For more information try --help
$ 04_02_parse_derive 0
? failed
error: Invalid value "0" for '<PORT>': 0 is not in 1..=65535
For more information try --help
```
A custom validator can be used to improve the error messages or provide additional validation:
A custom parser can be used to improve the error messages or provide additional validation:
[Example:](04_02_validate.rs)
```console