mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 14:22:34 +00:00
Merge pull request #3555 from epage/parse
docs(examples): More real-world parsing cases
This commit is contained in:
commit
38469060db
9 changed files with 159 additions and 68 deletions
|
@ -140,6 +140,7 @@ trybuild = "1.0.18"
|
|||
rustversion = "1"
|
||||
# Cutting out `filesystem` feature
|
||||
trycmd = { version = "0.13", default-features = false, features = ["color-auto", "diff", "examples"] }
|
||||
humantime = "2"
|
||||
|
||||
[[example]]
|
||||
name = "demo"
|
||||
|
@ -166,7 +167,7 @@ name = "git-derive"
|
|||
required-features = ["derive"]
|
||||
|
||||
[[example]]
|
||||
name = "keyvalue-derive"
|
||||
name = "typed-derive"
|
||||
required-features = ["derive"]
|
||||
|
||||
[[example]]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Examples
|
||||
|
||||
- Basic demo: [derive](demo.md)
|
||||
- Key-value pair arguments: [derive](keyvalue-derive.md)
|
||||
- Typed arguments: [derive](typed-derive.md)
|
||||
- Topics:
|
||||
- Custom `parse()`
|
||||
- Custom cargo command: [builder](cargo-example.md), [derive](cargo-example-derive.md)
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
*Jump to [source](keyvalue-derive.rs)*
|
||||
|
||||
**This requires enabling the `derive` feature flag.**
|
||||
|
||||
```console
|
||||
$ keyvalue-derive --help
|
||||
clap
|
||||
|
||||
USAGE:
|
||||
keyvalue-derive[EXE] [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
-D <DEFINES>
|
||||
-h, --help Print help information
|
||||
|
||||
$ keyvalue-derive -D Foo=10 -D Alice=30
|
||||
Args { defines: [("Foo", 10), ("Alice", 30)] }
|
||||
|
||||
$ keyvalue-derive -D Foo
|
||||
? failed
|
||||
error: Invalid value "Foo" for '-D <DEFINES>': invalid KEY=value: no `=` found in `Foo`
|
||||
|
||||
For more information try --help
|
||||
|
||||
$ keyvalue-derive -D Foo=Bar
|
||||
? failed
|
||||
error: Invalid value "Foo=Bar" for '-D <DEFINES>': invalid digit found in string
|
||||
|
||||
For more information try --help
|
||||
|
||||
```
|
|
@ -1,26 +1,16 @@
|
|||
// Note: this requires the `cargo` feature
|
||||
|
||||
use clap::{arg, command};
|
||||
use std::ops::RangeInclusive;
|
||||
use std::str::FromStr;
|
||||
|
||||
const PORT_RANGE: RangeInclusive<usize> = 1..=65535;
|
||||
use clap::{arg, command};
|
||||
|
||||
fn main() {
|
||||
let matches = command!()
|
||||
.arg(arg!(<PORT>).help("Network port to use").validator(|s| {
|
||||
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()
|
||||
)),
|
||||
})
|
||||
}))
|
||||
.arg(
|
||||
arg!(<PORT>)
|
||||
.help("Network port to use")
|
||||
.validator(port_in_range),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
// Note, it's safe to call unwrap() because the arg is required
|
||||
|
@ -29,3 +19,20 @@ fn main() {
|
|||
.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> {
|
||||
let port: usize = s
|
||||
.parse()
|
||||
.map_err(|_| format!("`{}` isn't a port number", s))?;
|
||||
if PORT_RANGE.contains(&port) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!(
|
||||
"Port not in range {}-{}",
|
||||
PORT_RANGE.start(),
|
||||
PORT_RANGE.end()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -497,6 +497,12 @@ OPTIONS:
|
|||
$ 04_02_validate 22
|
||||
PORT = 22
|
||||
|
||||
$ 04_02_validate foobar
|
||||
? failed
|
||||
error: Invalid value "foobar" for '<PORT>': `foobar` isn't a port number
|
||||
|
||||
For more information try --help
|
||||
|
||||
$ 04_02_validate 0
|
||||
? failed
|
||||
error: Invalid value "0" for '<PORT>': Port not in range 1-65535
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
use clap::Parser;
|
||||
use std::ops::RangeInclusive;
|
||||
use std::str::FromStr;
|
||||
|
||||
const PORT_RANGE: RangeInclusive<usize> = 1..=65535;
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
/// Network port to use
|
||||
#[clap(validator = port_in_range)]
|
||||
#[clap(parse(try_from_str=port_in_range))]
|
||||
port: usize,
|
||||
}
|
||||
|
||||
|
@ -18,16 +16,19 @@ fn main() {
|
|||
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()
|
||||
)),
|
||||
})
|
||||
const PORT_RANGE: RangeInclusive<usize> = 1..=65535;
|
||||
|
||||
fn port_in_range(s: &str) -> Result<usize, String> {
|
||||
let port: usize = s
|
||||
.parse()
|
||||
.map_err(|_| format!("`{}` isn't a port number", s))?;
|
||||
if PORT_RANGE.contains(&port) {
|
||||
Ok(port)
|
||||
} else {
|
||||
Err(format!(
|
||||
"Port not in range {}-{}",
|
||||
PORT_RANGE.start(),
|
||||
PORT_RANGE.end()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -419,8 +419,6 @@ For more information try --help
|
|||
|
||||
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
|
||||
|
@ -448,7 +446,7 @@ 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
|
||||
|
@ -469,6 +467,12 @@ OPTIONS:
|
|||
$ 04_02_validate_derive 22
|
||||
PORT = 22
|
||||
|
||||
$ 04_02_validate_derive foobar
|
||||
? failed
|
||||
error: Invalid value "foobar" for '<PORT>': `foobar` isn't a port number
|
||||
|
||||
For more information try --help
|
||||
|
||||
$ 04_02_validate_derive 0
|
||||
? failed
|
||||
error: Invalid value "0" for '<PORT>': Port not in range 1-65535
|
||||
|
|
86
examples/typed-derive.md
Normal file
86
examples/typed-derive.md
Normal file
|
@ -0,0 +1,86 @@
|
|||
*Jump to [source](typed-derive.rs)*
|
||||
|
||||
**This requires enabling the `derive` feature flag.**
|
||||
|
||||
Help:
|
||||
```console
|
||||
$ typed-derive --help
|
||||
clap
|
||||
|
||||
USAGE:
|
||||
typed-derive[EXE] [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--bind <BIND> Handle IP addresses
|
||||
-D <DEFINES> Hand-written parser for tuples
|
||||
-h, --help Print help information
|
||||
-I <DIR> Allow invalid UTF-8 paths
|
||||
-O <OPTIMIZATION> Implicitly using `std::str::FromStr`
|
||||
--sleep <SLEEP> Allow human-readable durations
|
||||
|
||||
```
|
||||
|
||||
Optimization-level (number)
|
||||
```console
|
||||
$ typed-derive -O 1
|
||||
Args { optimization: Some(1), include: None, bind: None, sleep: None, defines: [] }
|
||||
|
||||
$ typed-derive -O plaid
|
||||
? failed
|
||||
error: Invalid value "plaid" for '-O <OPTIMIZATION>': invalid digit found in string
|
||||
|
||||
For more information try --help
|
||||
|
||||
```
|
||||
|
||||
Include (path)
|
||||
```console
|
||||
$ typed-derive -I../hello
|
||||
Args { optimization: None, include: Some("../hello"), bind: None, sleep: None, defines: [] }
|
||||
|
||||
```
|
||||
|
||||
IP Address
|
||||
```console
|
||||
$ typed-derive --bind 192.0.0.1
|
||||
Args { optimization: None, include: None, bind: Some(192.0.0.1), sleep: None, defines: [] }
|
||||
|
||||
$ typed-derive --bind localhost
|
||||
? failed
|
||||
error: Invalid value "localhost" for '--bind <BIND>': invalid IP address syntax
|
||||
|
||||
For more information try --help
|
||||
|
||||
```
|
||||
|
||||
Time
|
||||
```console
|
||||
$ typed-derive --sleep 10s
|
||||
Args { optimization: None, include: None, bind: None, sleep: Some(Duration(10s)), defines: [] }
|
||||
|
||||
$ typed-derive --sleep forever
|
||||
? failed
|
||||
error: Invalid value "forever" for '--sleep <SLEEP>': expected number at 0
|
||||
|
||||
For more information try --help
|
||||
|
||||
```
|
||||
|
||||
Defines (key-value pairs)
|
||||
```console
|
||||
$ typed-derive -D Foo=10 -D Alice=30
|
||||
Args { optimization: None, include: None, bind: None, sleep: None, defines: [("Foo", 10), ("Alice", 30)] }
|
||||
|
||||
$ typed-derive -D Foo
|
||||
? failed
|
||||
error: Invalid value "Foo" for '-D <DEFINES>': invalid KEY=value: no `=` found in `Foo`
|
||||
|
||||
For more information try --help
|
||||
|
||||
$ typed-derive -D Foo=Bar
|
||||
? failed
|
||||
error: Invalid value "Foo=Bar" for '-D <DEFINES>': invalid digit found in string
|
||||
|
||||
For more information try --help
|
||||
|
||||
```
|
|
@ -5,6 +5,23 @@ use std::error::Error;
|
|||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Args {
|
||||
/// Implicitly using `std::str::FromStr`
|
||||
#[clap(short = 'O')]
|
||||
optimization: Option<usize>,
|
||||
|
||||
/// Allow invalid UTF-8 paths
|
||||
#[clap(short = 'I', parse(from_os_str), value_name = "DIR", value_hint = clap::ValueHint::DirPath)]
|
||||
include: Option<std::path::PathBuf>,
|
||||
|
||||
/// Handle IP addresses
|
||||
#[clap(long)]
|
||||
bind: Option<std::net::IpAddr>,
|
||||
|
||||
/// Allow human-readable durations
|
||||
#[clap(long)]
|
||||
sleep: Option<humantime::Duration>,
|
||||
|
||||
/// Hand-written parser for tuples
|
||||
#[clap(short = 'D', parse(try_from_str = parse_key_val), multiple_occurrences(true))]
|
||||
defines: Vec<(String, i32)>,
|
||||
}
|
Loading…
Reference in a new issue