Skip to content

Commit 9fd4dc9

Browse files
committed
docs(cookbook): Provide a custom TypedValueParser
1 parent 8f8e861 commit 9fd4dc9

File tree

6 files changed

+143
-2
lines changed

6 files changed

+143
-2
lines changed

Cargo.lock

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ snapbox = { version = "0.6.16", features = ["term-svg"] }
199199
shlex = "1.3.0"
200200
automod = "1.0.14"
201201
clap-cargo = { version = "0.15.0", default-features = false }
202+
semver = "1.0.26"
202203

203204
[[example]]
204205
name = "demo"

examples/typed-derive/custom.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
Help:
2+
```console
3+
$ typed-derive custom --help
4+
Usage: typed-derive custom [OPTIONS]
5+
6+
Options:
7+
--target-version <TARGET_VERSION>
8+
Hand-implement `TypedValueParser`
9+
10+
Possible values:
11+
- major: Increase the major version (x.0.0)
12+
- minor: Increase the minor version (x.y.0)
13+
- patch: Increase the patch version (x.y.z)
14+
15+
-h, --help
16+
Print help (see a summary with '-h')
17+
18+
```
19+
20+
Defines (key-value pairs)
21+
```console
22+
$ typed-derive custom --target-version major
23+
Custom(CustomParser { target_version: Some(Relative(Major)) })
24+
25+
$ typed-derive custom --target-version 10.0.0
26+
Custom(CustomParser { target_version: Some(Absolute(Version { major: 10, minor: 0, patch: 0 })) })
27+
28+
$ typed-derive custom --target-version 10
29+
? failed
30+
error: invalid value '10' for '--target-version <TARGET_VERSION>': unexpected end of input while parsing major version number
31+
32+
For more information, try '--help'.
33+
34+
$ typed-derive custom --target-version blue
35+
? failed
36+
error: invalid value 'blue' for '--target-version <TARGET_VERSION>': unexpected character 'b' while parsing major version number
37+
38+
For more information, try '--help'.
39+
40+
```

examples/typed-derive/custom.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use clap::Args;
2+
3+
use crate::implicit::BumpLevel;
4+
5+
#[derive(Args, Debug)]
6+
pub(crate) struct CustomParser {
7+
/// Hand-implement `TypedValueParser`
8+
#[arg(long)]
9+
target_version: Option<TargetVersion>,
10+
}
11+
12+
/// Enum or custom value
13+
#[derive(Clone, Debug)]
14+
pub(crate) enum TargetVersion {
15+
Relative(BumpLevel),
16+
Absolute(semver::Version),
17+
}
18+
19+
impl std::fmt::Display for TargetVersion {
20+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
21+
match self {
22+
TargetVersion::Relative(bump_level) => {
23+
write!(f, "{bump_level}")
24+
}
25+
TargetVersion::Absolute(version) => {
26+
write!(f, "{version}")
27+
}
28+
}
29+
}
30+
}
31+
32+
impl std::str::FromStr for TargetVersion {
33+
type Err = String;
34+
35+
fn from_str(s: &str) -> Result<Self, Self::Err> {
36+
if let Ok(bump_level) = BumpLevel::from_str(s) {
37+
Ok(TargetVersion::Relative(bump_level))
38+
} else {
39+
Ok(TargetVersion::Absolute(
40+
semver::Version::parse(s).map_err(|e| e.to_string())?,
41+
))
42+
}
43+
}
44+
}
45+
46+
/// Default to `TargetVersionParser` for `TargetVersion`, instead of `FromStr`
47+
impl clap::builder::ValueParserFactory for TargetVersion {
48+
type Parser = TargetVersionParser;
49+
50+
fn value_parser() -> Self::Parser {
51+
TargetVersionParser
52+
}
53+
}
54+
55+
#[derive(Copy, Clone)]
56+
pub(crate) struct TargetVersionParser;
57+
58+
impl clap::builder::TypedValueParser for TargetVersionParser {
59+
type Value = TargetVersion;
60+
61+
fn parse_ref(
62+
&self,
63+
cmd: &clap::Command,
64+
arg: Option<&clap::Arg>,
65+
value: &std::ffi::OsStr,
66+
) -> Result<Self::Value, clap::Error> {
67+
let inner_parser = <TargetVersion as std::str::FromStr>::from_str;
68+
inner_parser.parse_ref(cmd, arg, value)
69+
}
70+
71+
fn possible_values(
72+
&self,
73+
) -> Option<Box<dyn Iterator<Item = clap::builder::PossibleValue> + '_>> {
74+
let inner_parser = clap::builder::EnumValueParser::<BumpLevel>::new();
75+
#[allow(clippy::needless_collect)] // Erasing a lifetime
76+
inner_parser.possible_values().map(|ps| {
77+
let ps = ps.collect::<Vec<_>>();
78+
let ps: Box<dyn Iterator<Item = clap::builder::PossibleValue> + '_> =
79+
Box::new(ps.into_iter());
80+
ps
81+
})
82+
}
83+
}

examples/typed-derive/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use clap::Parser;
22

33
mod builtin;
4+
mod custom;
45
mod fn_parser;
56
mod foreign_crate;
67
mod implicit;
@@ -11,6 +12,7 @@ enum Cli {
1112
Implicit(implicit::ImplicitParsers),
1213
Builtin(builtin::BuiltInParsers),
1314
FnParser(fn_parser::FnParser),
15+
Custom(custom::CustomParser),
1416
}
1517

1618
fn main() {

src/_cookbook/typed_derive.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,18 @@
1818
//!
1919
#![doc = include_str!("../../examples/typed-derive/builtin.md")]
2020
//!
21-
//! ## Custom [`TypedValueParser`][crate::builder::TypedValueParser]
21+
//! ## Custom parser function
2222
//!
2323
//! ```rust
2424
#![doc = include_str!("../../examples/typed-derive/fn_parser.rs")]
2525
//! ```
2626
//!
2727
#![doc = include_str!("../../examples/typed-derive/fn_parser.md")]
28+
//!
29+
//! ## Custom [`TypedValueParser`][crate::builder::TypedValueParser]
30+
//!
31+
//! ```rust
32+
#![doc = include_str!("../../examples/typed-derive/custom.rs")]
33+
//! ```
34+
//!
35+
#![doc = include_str!("../../examples/typed-derive/custom.md")]

0 commit comments

Comments
 (0)