docs(tutorial): Demonstrate custom parsing

Adds a more in depth validator to validate that the port is in range in the derive and builder tutorial (section 4.2).

This supersedes #3416
This commit is contained in:
josh rotenberg 2022-02-07 20:50:39 -08:00 committed by Ed Page
parent 1b5f9e6450
commit ee3eab1614
7 changed files with 143 additions and 11 deletions

View file

@ -240,6 +240,11 @@ name = "04_01_enum"
path = "examples/tutorial_builder/04_01_enum.rs" path = "examples/tutorial_builder/04_01_enum.rs"
required-features = ["cargo", "derive"] required-features = ["cargo", "derive"]
[[example]]
name = "04_02_parse"
path = "examples/tutorial_builder/04_02_parse.rs"
required-features = ["cargo"]
[[example]] [[example]]
name = "04_02_validate" name = "04_02_validate"
path = "examples/tutorial_builder/04_02_validate.rs" path = "examples/tutorial_builder/04_02_validate.rs"
@ -316,6 +321,11 @@ name = "04_01_enum_derive"
path = "examples/tutorial_derive/04_01_enum.rs" path = "examples/tutorial_derive/04_01_enum.rs"
required-features = ["derive"] required-features = ["derive"]
[[example]]
name = "04_02_parse_derive"
path = "examples/tutorial_derive/04_02_parse.rs"
required-features = ["derive"]
[[example]] [[example]]
name = "04_02_validate_derive" name = "04_02_validate_derive"
path = "examples/tutorial_derive/04_02_validate.rs" path = "examples/tutorial_derive/04_02_validate.rs"

View file

@ -0,0 +1,17 @@
use clap::{app_from_crate, arg};
fn main() {
let matches = app_from_crate!()
.arg(
arg!(<PORT>)
.help("Network port to use")
.validator(|s| s.parse::<usize>()),
)
.get_matches();
// Note, it's safe to call unwrap() because the arg is required
let port: usize = matches
.value_of_t("PORT")
.expect("'PORT' is required and parsing will fail if its missing");
println!("PORT = {}", port);
}

View file

@ -1,12 +1,24 @@
use clap::{app_from_crate, arg}; use clap::{app_from_crate, arg};
use std::ops::RangeInclusive;
use std::str::FromStr;
const PORT_RANGE: RangeInclusive<usize> = 1..=65535;
fn main() { fn main() {
let matches = app_from_crate!() let matches = app_from_crate!()
.arg( .arg(arg!(<PORT>).help("Network port to use").validator(|s| {
arg!(<PORT>) usize::from_str(s)
.help("Network port to use") .map(|port| PORT_RANGE.contains(&port))
.validator(|s| s.parse::<usize>()), .map_err(|e| e.to_string())
) .and_then(|result| match result {
true => Ok(()),
false => Err(format!(
"Port not in range {}-{}",
PORT_RANGE.start(),
PORT_RANGE.end()
)),
})
}))
.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

View file

@ -449,7 +449,36 @@ For more information try --help
### Validated values ### Validated values
More generally, you can validate and parse into any data type. More generally, you can parse into any data type.
[Example:](04_02_parse.rs)
```console
$ 04_02_parse --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
04_02_parse[EXE] <PORT>
ARGS:
<PORT> Network port to use
OPTIONS:
-h, --help Print help information
-V, --version Print version information
$ 04_02_parse 22
PORT = 22
$ 04_02_parse foobar
? failed
error: Invalid value "foobar" for '<PORT>': invalid digit found in string
For more information try --help
```
A custom validator can be used to improve the error messages or provide additional validation:
[Example:](04_02_validate.rs) [Example:](04_02_validate.rs)
```console ```console
@ -470,9 +499,9 @@ OPTIONS:
$ 04_02_validate 22 $ 04_02_validate 22
PORT = 22 PORT = 22
$ 04_02_validate foobar $ 04_02_validate 0
? failed ? failed
error: Invalid value "foobar" for '<PORT>': invalid digit found in string error: Invalid value "0" for '<PORT>': Port not in range 1-65535
For more information try --help For more information try --help

View file

@ -0,0 +1,15 @@
use clap::Parser;
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
/// Network port to use
#[clap(parse(try_from_str))]
port: usize,
}
fn main() {
let cli = Cli::parse();
println!("PORT = {}", cli.port);
}

View file

@ -1,10 +1,14 @@
use clap::Parser; use clap::Parser;
use std::ops::RangeInclusive;
use std::str::FromStr;
const PORT_RANGE: RangeInclusive<usize> = 1..=65535;
#[derive(Parser)] #[derive(Parser)]
#[clap(author, version, about, long_about = None)] #[clap(author, version, about, long_about = None)]
struct Cli { struct Cli {
/// Network port to use /// Network port to use
#[clap(parse(try_from_str))] #[clap(validator = port_in_range)]
port: usize, port: usize,
} }
@ -13,3 +17,17 @@ fn main() {
println!("PORT = {}", cli.port); println!("PORT = {}", cli.port);
} }
fn port_in_range(s: &str) -> Result<(), String> {
usize::from_str(s)
.map(|port| PORT_RANGE.contains(&port))
.map_err(|e| e.to_string())
.and_then(|result| match result {
true => Ok(()),
false => Err(format!(
"Port not in range {}-{}",
PORT_RANGE.start(),
PORT_RANGE.end()
)),
})
}

View file

@ -417,6 +417,37 @@ For more information try --help
More generally, you can validate and parse into any data type. More generally, you can validate and parse into any data type.
More generally, you can parse into any data type.
[Example:](04_02_parse.rs)
```console
$ 04_02_parse_derive --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
04_02_parse_derive[EXE] <PORT>
ARGS:
<PORT> Network port to use
OPTIONS:
-h, --help Print help information
-V, --version Print version information
$ 04_02_parse_derive 22
PORT = 22
$ 04_02_parse_derive foobar
? failed
error: Invalid value "foobar" for '<PORT>': invalid digit found in string
For more information try --help
```
A custom validator can be used to improve the error messages or provide additional validation:
[Example:](04_02_validate.rs) [Example:](04_02_validate.rs)
```console ```console
$ 04_02_validate_derive --help $ 04_02_validate_derive --help
@ -436,9 +467,9 @@ OPTIONS:
$ 04_02_validate_derive 22 $ 04_02_validate_derive 22
PORT = 22 PORT = 22
$ 04_02_validate_derive foobar $ 04_02_validate_derive 0
? failed ? failed
error: Invalid value "foobar" for '<PORT>': invalid digit found in string error: Invalid value "0" for '<PORT>': Port not in range 1-65535
For more information try --help For more information try --help