typed_derive/
custom.rs

1use clap::Args;
2
3use crate::implicit::BumpLevel;
4
5#[derive(Args, Debug)]
6pub(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)]
14pub(crate) enum TargetVersion {
15    Relative(BumpLevel),
16    Absolute(semver::Version),
17}
18
19impl 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
32impl 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`
47impl 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)]
56pub(crate) struct TargetVersionParser;
57
58impl 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}