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: Then to directly invoke the command, run:
```console ```console
$ cargo-example example $ cargo-example example
None Ok(None)
$ cargo-example example --manifest-path Cargo.toml $ 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::command!("example").arg(
clap::arg!(--"manifest-path" <PATH>) clap::arg!(--"manifest-path" <PATH>)
.required(false) .required(false)
.allow_invalid_utf8(true), .value_parser(clap::value_parser!(std::path::PathBuf)),
), ),
); );
let matches = cmd.get_matches(); let matches = cmd.get_matches();
@ -16,8 +16,6 @@ fn main() {
Some(("example", matches)) => matches, Some(("example", matches)) => matches,
_ => unreachable!("clap should ensure we don't get here"), _ => unreachable!("clap should ensure we don't get here"),
}; };
let manifest_path = matches let manifest_path = matches.get_one::<std::path::PathBuf>("manifest-path");
.value_of_os("manifest-path")
.map(std::path::PathBuf::from);
println!("{:?}", manifest_path); println!("{:?}", manifest_path);
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -71,13 +71,23 @@ fn main() {
match matches.subcommand() { match matches.subcommand() {
Some(("sync", sync_matches)) => { Some(("sync", sync_matches)) => {
if sync_matches.is_present("search") { 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(", "); let values = packages.join(", ");
println!("Searching for {}...", values); println!("Searching for {}...", values);
return; 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(", "); let values = packages.join(", ");
if sync_matches.is_present("info") { if sync_matches.is_present("info") {
@ -87,11 +97,17 @@ fn main() {
} }
} }
Some(("query", query_matches)) => { Some(("query", query_matches)) => {
if let Some(packages) = query_matches.values_of("info") { if let Some(packages) = query_matches
let comma_sep = packages.collect::<Vec<_>>().join(", "); .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); println!("Retrieving info for {}...", comma_sep);
} else if let Some(queries) = query_matches.values_of("search") { } else if let Some(queries) = query_matches
let comma_sep = queries.collect::<Vec<_>>().join(", "); .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); println!("Searching Locally for {}...", comma_sep);
} else { } else {
println!("Displaying all locally installed packages..."); println!("Displaying all locally installed packages...");

View file

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

View file

@ -11,6 +11,18 @@ fn main() {
.arg(arg!(--one <VALUE>)) .arg(arg!(--one <VALUE>))
.get_matches(); .get_matches();
println!("two: {:?}", matches.value_of("two").expect("required")); println!(
println!("one: {:?}", matches.value_of("one").expect("required")); "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>)) .arg(arg!(--one <VALUE>))
.get_matches(); .get_matches();
println!("two: {:?}", matches.value_of("two").expect("required")); println!(
println!("one: {:?}", matches.value_of("one").expect("required")); "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>)) .arg(arg!(--one <VALUE>))
.get_matches(); .get_matches();
println!("two: {:?}", matches.value_of("two").expect("required")); println!(
println!("one: {:?}", matches.value_of("one").expect("required")); "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)) .arg(arg!(-n --name <NAME>).required(false))
.get_matches(); .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() { fn main() {
let matches = command!().arg(arg!([NAME])).get_matches(); 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() { match matches.subcommand() {
Some(("add", sub_matches)) => println!( Some(("add", sub_matches)) => println!(
"'myapp add' was used, name is: {:?}", "'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`"), _ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"),
} }

View file

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

View file

@ -1,6 +1,6 @@
// Note: this requires the `cargo` feature // 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)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ArgEnum)]
enum Mode { enum Mode {
@ -8,48 +8,19 @@ enum Mode {
Slow, 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() { fn main() {
let matches = command!() let matches = command!()
.arg( .arg(
arg!(<MODE>) arg!(<MODE>)
.help("What mode to run the program in") .help("What mode to run the program in")
.possible_values(Mode::possible_values()), .value_parser(value_parser!(Mode)),
) )
.get_matches(); .get_matches();
// Note, it's safe to call unwrap() because the arg is required // Note, it's safe to call unwrap() because the arg is required
match matches match matches
.value_of_t("MODE") .get_one::<Mode>("MODE")
.expect("matches definition")
.expect("'MODE' is required and parsing will fail if its missing") .expect("'MODE' is required and parsing will fail if its missing")
{ {
Mode::Fast => { Mode::Fast => {

View file

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

View file

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

View file

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

View file

@ -1,6 +1,8 @@
// Note: this requires the `cargo` feature // Note: this requires the `cargo` feature
use clap::{arg, command, ArgGroup}; use std::path::PathBuf;
use clap::{arg, command, value_parser, ArgGroup};
fn main() { fn main() {
// Create application like normal // Create application like normal
@ -18,15 +20,25 @@ fn main() {
) )
// Arguments can also be added to a group individually, these two arguments // Arguments can also be added to a group individually, these two arguments
// are part of the "input" group which is not required // 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(
arg!(--"spec-in" <SPEC_IN> "some special input argument") arg!(--"spec-in" <SPEC_IN> "some special input argument")
.required(false) .required(false)
.value_parser(value_parser!(PathBuf))
.group("input"), .group("input"),
) )
// Now let's assume we have a -c [config] argument which requires one of // Now let's assume we have a -c [config] argument which requires one of
// (but **not** both) the "input" arguments // (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(); .get_matches();
// Let's assume the old version 1.2.3 // Let's assume the old version 1.2.3
@ -35,8 +47,11 @@ fn main() {
let mut patch = 3; let mut patch = 3;
// See if --set-ver was used to set the version manually // 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
ver.to_string() .get_one::<String>("set-ver")
.expect("matches definition")
{
ver.to_owned()
} else { } else {
// Increment the one requested (in a real program, we'd reset the lower numbers) // Increment the one requested (in a real program, we'd reset the lower numbers)
let (maj, min, pat) = ( let (maj, min, pat) = (
@ -58,12 +73,23 @@ fn main() {
// Check for usage of -c // Check for usage of -c
if matches.is_present("config") { if matches.is_present("config") {
let input = matches let input = matches
.value_of("INPUT_FILE") .get_one::<PathBuf>("INPUT_FILE")
.unwrap_or_else(|| matches.value_of("spec-in").unwrap()); .expect("matches definition")
.unwrap_or_else(|| {
matches
.get_one::<PathBuf>("spec-in")
.expect("matches definition")
.unwrap()
})
.display();
println!( println!(
"Doing work using input {} and config {}", "Doing work using input {} and config {}",
input, 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 // Note: this requires the `cargo` feature
use clap::{arg, command, ErrorKind}; use std::path::PathBuf;
use clap::{arg, command, value_parser, ErrorKind};
fn main() { fn main() {
// Create application like normal // Create application like normal
@ -12,11 +14,19 @@ fn main() {
.arg(arg!(--patch "auto inc patch")) .arg(arg!(--patch "auto inc patch"))
// Arguments can also be added to a group individually, these two arguments // Arguments can also be added to a group individually, these two arguments
// are part of the "input" group which is not required // are part of the "input" group which is not required
.arg(arg!([INPUT_FILE] "some regular input")) .arg(arg!([INPUT_FILE] "some regular input").value_parser(value_parser!(PathBuf)))
.arg(arg!(--"spec-in" <SPEC_IN> "some special input argument").required(false)) .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 // Now let's assume we have a -c [config] argument which requires one of
// (but **not** both) the "input" arguments // (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 matches = cmd.get_matches_mut();
// Let's assume the old version 1.2.3 // Let's assume the old version 1.2.3
@ -25,7 +35,10 @@ fn main() {
let mut patch = 3; let mut patch = 3;
// See if --set-ver was used to set the version manually // 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") if matches.is_present("major") || matches.is_present("minor") || matches.is_present("patch")
{ {
cmd.error( cmd.error(
@ -62,19 +75,29 @@ fn main() {
// Check for usage of -c // Check for usage of -c
if matches.is_present("config") { if matches.is_present("config") {
let input = matches let input = matches
.value_of("INPUT_FILE") .get_one::<PathBuf>("INPUT_FILE")
.or_else(|| matches.value_of("spec-in")) .expect("matches definition")
.or_else(|| {
matches
.get_one::<PathBuf>("spec-in")
.expect("matches definition")
})
.unwrap_or_else(|| { .unwrap_or_else(|| {
cmd.error( cmd.error(
ErrorKind::MissingRequiredArgument, ErrorKind::MissingRequiredArgument,
"INPUT_FILE or --spec-in is required when using --config", "INPUT_FILE or --spec-in is required when using --config",
) )
.exit() .exit()
}); })
.display();
println!( println!(
"Doing work using input {} and config {}", "Doing work using input {} and config {}",
input, 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 // Note: this requires the `cargo` feature
use clap::{arg, command}; use clap::{arg, command, value_parser};
fn main() { fn main() {
let matches = cmd().get_matches(); let matches = cmd().get_matches();
// Note, it's safe to call unwrap() because the arg is required // Note, it's safe to call unwrap() because the arg is required
let port: usize = matches let port: usize = *matches
.value_of_t("PORT") .get_one::<usize>("PORT")
.expect("matches definition")
.expect("'PORT' is required and parsing will fail if its missing"); .expect("'PORT' is required and parsing will fail if its missing");
println!("PORT = {}", port); println!("PORT = {}", port);
} }
@ -16,7 +17,7 @@ fn cmd() -> clap::Command<'static> {
command!().arg( command!().arg(
arg!(<PORT>) arg!(<PORT>)
.help("Network port to use") .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 ### 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) [Example:](04_02_parse.rs)
```console ```console
@ -471,9 +471,15 @@ error: Invalid value "foobar" for '<PORT>': invalid digit found in string
For more information try --help 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) [Example:](04_02_validate.rs)
```console ```console