use clap::ArgMatches;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
pub trait ArgMatchesExt {
fn required_str(&self, name: &str) -> &str;
fn optional_str(&self, name: &str) -> Option<&str>;
fn required_string(&self, name: &str) -> String;
fn optional_string(&self, name: &str) -> Option<String>;
fn parse_required<T>(&self, name: &str) -> Result<T, ArgParseError>
where
T: FromStr,
T::Err: Display;
fn parse_optional<T>(&self, name: &str) -> Result<Option<T>, ArgParseError>
where
T: FromStr,
T::Err: Display;
fn get_typed<T: Clone + Send + Sync + 'static>(&self, name: &str) -> Option<T>;
fn get_typed_or<T: Clone + Send + Sync + 'static>(&self, name: &str, default: T) -> T;
}
#[derive(Debug)]
pub struct ArgParseError {
pub name: String,
pub message: String,
}
impl Display for ArgParseError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Invalid value for '{}': {}", self.name, self.message)
}
}
impl Error for ArgParseError {}
impl ArgMatchesExt for ArgMatches {
fn required_str(&self, name: &str) -> &str {
self.get_one::<String>(name)
.map(|s| s.as_str())
.unwrap_or_else(|| panic!("Required argument '{}' not provided", name))
}
fn optional_str(&self, name: &str) -> Option<&str> {
self.get_one::<String>(name).map(|s| s.as_str())
}
fn required_string(&self, name: &str) -> String {
self.required_str(name).to_string()
}
fn optional_string(&self, name: &str) -> Option<String> {
self.optional_str(name).map(|s| s.to_string())
}
fn parse_required<T>(&self, name: &str) -> Result<T, ArgParseError>
where
T: FromStr,
T::Err: Display,
{
let value = self.required_str(name);
value.parse::<T>().map_err(|e| ArgParseError {
name: name.to_string(),
message: e.to_string(),
})
}
fn parse_optional<T>(&self, name: &str) -> Result<Option<T>, ArgParseError>
where
T: FromStr,
T::Err: Display,
{
match self.optional_str(name) {
Some(value) => value.parse::<T>().map(Some).map_err(|e| ArgParseError {
name: name.to_string(),
message: e.to_string(),
}),
None => Ok(None),
}
}
fn get_typed<T: Clone + Send + Sync + 'static>(&self, name: &str) -> Option<T> {
self.get_one::<T>(name).cloned()
}
fn get_typed_or<T: Clone + Send + Sync + 'static>(&self, name: &str, default: T) -> T {
self.get_typed(name).unwrap_or(default)
}
}