use anyhow::{Context, Result};
use clap::ArgMatches;
pub trait ArgMatchesExt {
fn req_str(&self, name: &'static str) -> Result<&str>;
fn opt_str(&self, name: &'static str) -> Option<&str>;
fn opt_string(&self, name: &'static str) -> Option<String>;
}
impl ArgMatchesExt for ArgMatches {
fn req_str(&self, name: &'static str) -> Result<&str> {
self
.get_one::<String>(name)
.map(String::as_str)
.with_context(|| format!("'{name}' argument is mandatory"))
}
fn opt_str(&self, name: &'static str) -> Option<&str> {
self.get_one::<String>(name).map(String::as_str)
}
fn opt_string(&self, name: &'static str) -> Option<String> {
self.get_one::<String>(name).cloned()
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::{Arg, Command};
fn cmd() -> Command {
Command::new("test")
.arg(Arg::new("required").long("required"))
.arg(Arg::new("optional").long("optional"))
}
#[test]
fn req_str_returns_value_when_present() {
let m = cmd().get_matches_from(["test", "--required", "abc"]);
assert_eq!(m.req_str("required").unwrap(), "abc");
}
#[test]
fn req_str_errors_when_missing_with_name_in_message() {
let m = cmd().get_matches_from(["test"]);
let err = m.req_str("required").unwrap_err().to_string();
assert!(err.contains("'required'"), "got: {err}");
assert!(err.contains("mandatory"), "got: {err}");
}
#[test]
fn opt_str_returns_some_when_present() {
let m = cmd().get_matches_from(["test", "--optional", "xyz"]);
assert_eq!(m.opt_str("optional"), Some("xyz"));
}
#[test]
fn opt_str_returns_none_when_missing() {
let m = cmd().get_matches_from(["test"]);
assert_eq!(m.opt_str("optional"), None);
}
#[test]
fn opt_string_returns_owned_when_present() {
let m = cmd().get_matches_from(["test", "--optional", "owned"]);
assert_eq!(m.opt_string("optional"), Some("owned".to_string()));
}
}