| use clap::builder::TypedValueParser as _; |
| use clap::Parser; |
| use std::error::Error; |
| |
| #[derive(Parser, Debug)] // requires `derive` feature |
| #[command(term_width = 0)] // Just to make testing across clap features easier |
| struct Args { |
| /// Implicitly using `std::str::FromStr` |
| #[arg(short = 'O')] |
| optimization: Option<usize>, |
| |
| /// Allow invalid UTF-8 paths |
| #[arg(short = 'I', value_name = "DIR", value_hint = clap::ValueHint::DirPath)] |
| include: Option<std::path::PathBuf>, |
| |
| /// Handle IP addresses |
| #[arg(long)] |
| bind: Option<std::net::IpAddr>, |
| |
| /// Allow human-readable durations |
| #[arg(long)] |
| sleep: Option<humantime::Duration>, |
| |
| /// Hand-written parser for tuples |
| #[arg(short = 'D', value_parser = parse_key_val::<String, i32>)] |
| defines: Vec<(String, i32)>, |
| |
| /// Support for discrete numbers |
| #[arg( |
| long, |
| default_value_t = 22, |
| value_parser = clap::builder::PossibleValuesParser::new(["22", "80"]) |
| .map(|s| s.parse::<usize>().unwrap()), |
| )] |
| port: usize, |
| |
| /// Support enums from a foreign crate that don't implement `ValueEnum` |
| #[arg( |
| long, |
| default_value_t = foreign_crate::LogLevel::Info, |
| value_parser = clap::builder::PossibleValuesParser::new(["info", "debug", "info", "warn", "error"]) |
| .map(|s| s.parse::<foreign_crate::LogLevel>().unwrap()), |
| )] |
| log_level: foreign_crate::LogLevel, |
| } |
| |
| /// Parse a single key-value pair |
| fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>> |
| where |
| T: std::str::FromStr, |
| T::Err: Error + Send + Sync + 'static, |
| U: std::str::FromStr, |
| U::Err: Error + Send + Sync + 'static, |
| { |
| let pos = s |
| .find('=') |
| .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?; |
| Ok((s[..pos].parse()?, s[pos + 1..].parse()?)) |
| } |
| |
| mod foreign_crate { |
| #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
| pub enum LogLevel { |
| Trace, |
| Debug, |
| Info, |
| Warn, |
| Error, |
| } |
| |
| impl std::fmt::Display for LogLevel { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| let s = match self { |
| Self::Trace => "trace", |
| Self::Debug => "debug", |
| Self::Info => "info", |
| Self::Warn => "warn", |
| Self::Error => "error", |
| }; |
| s.fmt(f) |
| } |
| } |
| impl std::str::FromStr for LogLevel { |
| type Err = String; |
| |
| fn from_str(s: &str) -> Result<Self, Self::Err> { |
| match s { |
| "trace" => Ok(Self::Trace), |
| "debug" => Ok(Self::Debug), |
| "info" => Ok(Self::Info), |
| "warn" => Ok(Self::Warn), |
| "error" => Ok(Self::Error), |
| _ => Err(format!("Unknown log level: {s}")), |
| } |
| } |
| } |
| } |
| |
| fn main() { |
| let args = Args::parse(); |
| println!("{args:?}"); |
| } |