1 | use clap::builder::TypedValueParser as _; |
2 | use clap::Parser; |
3 | use std::error::Error; |
4 | |
5 | #[derive(Parser, Debug)] // requires `derive` feature |
6 | #[command(term_width = 0)] // Just to make testing across clap features easier |
7 | struct Args { |
8 | /// Implicitly using `std::str::FromStr` |
9 | #[arg(short = 'O' )] |
10 | optimization: Option<usize>, |
11 | |
12 | /// Allow invalid UTF-8 paths |
13 | #[arg(short = 'I' , value_name = "DIR" , value_hint = clap::ValueHint::DirPath)] |
14 | include: Option<std::path::PathBuf>, |
15 | |
16 | /// Handle IP addresses |
17 | #[arg(long)] |
18 | bind: Option<std::net::IpAddr>, |
19 | |
20 | /// Allow human-readable durations |
21 | #[arg(long)] |
22 | sleep: Option<humantime::Duration>, |
23 | |
24 | /// Hand-written parser for tuples |
25 | #[arg(short = 'D' , value_parser = parse_key_val::<String, i32>)] |
26 | defines: Vec<(String, i32)>, |
27 | |
28 | /// Support for discrete numbers |
29 | #[arg( |
30 | long, |
31 | default_value_t = 22, |
32 | value_parser = clap::builder::PossibleValuesParser::new(["22" , "80" ]) |
33 | .map(|s| s.parse::<usize>().unwrap()), |
34 | )] |
35 | port: usize, |
36 | |
37 | /// Support enums from a foreign crate that don't implement `ValueEnum` |
38 | #[arg( |
39 | long, |
40 | default_value_t = foreign_crate::LogLevel::Info, |
41 | value_parser = clap::builder::PossibleValuesParser::new(["trace" , "debug" , "info" , "warn" , "error" ]) |
42 | .map(|s| s.parse::<foreign_crate::LogLevel>().unwrap()), |
43 | )] |
44 | log_level: foreign_crate::LogLevel, |
45 | } |
46 | |
47 | /// Parse a single key-value pair |
48 | fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>> |
49 | where |
50 | T: std::str::FromStr, |
51 | T::Err: Error + Send + Sync + 'static, |
52 | U: std::str::FromStr, |
53 | U::Err: Error + Send + Sync + 'static, |
54 | { |
55 | let pos = s |
56 | .find('=' ) |
57 | .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`" ))?; |
58 | Ok((s[..pos].parse()?, s[pos + 1..].parse()?)) |
59 | } |
60 | |
61 | mod foreign_crate { |
62 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
63 | pub enum LogLevel { |
64 | Trace, |
65 | Debug, |
66 | Info, |
67 | Warn, |
68 | Error, |
69 | } |
70 | |
71 | impl std::fmt::Display for LogLevel { |
72 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
73 | let s = match self { |
74 | Self::Trace => "trace" , |
75 | Self::Debug => "debug" , |
76 | Self::Info => "info" , |
77 | Self::Warn => "warn" , |
78 | Self::Error => "error" , |
79 | }; |
80 | s.fmt(f) |
81 | } |
82 | } |
83 | impl std::str::FromStr for LogLevel { |
84 | type Err = String; |
85 | |
86 | fn from_str(s: &str) -> Result<Self, Self::Err> { |
87 | match s { |
88 | "trace" => Ok(Self::Trace), |
89 | "debug" => Ok(Self::Debug), |
90 | "info" => Ok(Self::Info), |
91 | "warn" => Ok(Self::Warn), |
92 | "error" => Ok(Self::Error), |
93 | _ => Err(format!("Unknown log level: {s}" )), |
94 | } |
95 | } |
96 | } |
97 | } |
98 | |
99 | fn main() { |
100 | let args = Args::parse(); |
101 | println!("{args:?}" ); |
102 | } |
103 | |