use pico_args::Arguments;
pub struct Args {
pub config: String,
pub dir: Option<String>,
#[cfg(feature = "log")]
pub verbose: u8,
}
pub fn print_help() {
println!("lazydns {}\n", env!("CARGO_PKG_VERSION"));
println!("Usage: lazydns [OPTIONS]\n");
println!("OPTIONS:");
println!(" -c, --config <file> Configuration file path (default: config.yaml)");
println!(" -d, --dir <dir> Working directory");
#[cfg(feature = "log")]
println!(
" -v, --verbose <count> Verbosity: -v (debug), -vv (trace), -vvv (trace + external crate logs)"
);
println!(" -V, --version Print version and exit");
println!(" -h, --help Print this help message");
}
pub fn parse_args() -> Option<Args> {
let raw_args: Vec<String> = std::env::args().collect();
parse_args_from_vec(raw_args)
}
pub fn parse_args_from_vec(raw_args: Vec<String>) -> Option<Args> {
if raw_args.len() <= 1 {
print_help();
return None;
}
let os_args: Vec<std::ffi::OsString> = raw_args
.iter()
.cloned()
.map(std::ffi::OsString::from)
.collect();
let mut pargs = Arguments::from_vec(os_args);
if pargs.contains(["-h", "--help"]) {
print_help();
return None;
}
let config = match pargs.opt_value_from_str(["-c", "--config"]) {
Ok(Some(s)) => s,
_ => "config.yaml".to_string(),
};
let dir = match pargs.opt_value_from_str(["-d", "--dir"]) {
Ok(Some(s)) => Some(s),
_ => None,
};
#[cfg(feature = "log")]
let mut verbose_count: u8 = 0;
#[cfg(feature = "log")]
for arg in &raw_args[1..] {
if arg == "-v" || arg == "--verbose" {
verbose_count = verbose_count.saturating_add(1);
} else if arg.starts_with("-v") && arg.chars().skip(1).all(|c| c == 'v') {
verbose_count = verbose_count.saturating_add(arg.chars().skip(1).count() as u8);
}
}
let version = pargs.contains(["-V", "--version"]);
if version {
println!("lazydns {}", env!("CARGO_PKG_VERSION"));
return None;
}
Some(Args {
config,
dir,
#[cfg(feature = "log")]
verbose: verbose_count.min(3),
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn returns_none_and_prints_help_with_no_args() {
let args = vec!["lazydns".to_string()];
let res = parse_args_from_vec(args);
assert!(res.is_none());
}
#[test]
fn returns_none_on_help_flag() {
let args = vec!["lazydns".to_string(), "--help".to_string()];
let res = parse_args_from_vec(args);
assert!(res.is_none());
}
#[cfg(feature = "log")]
#[test]
fn parses_verbose_variants_and_config() {
let args = vec![
"lazydns".to_string(),
"-c".to_string(),
"myconf.yaml".to_string(),
"-d".to_string(),
"/tmp".to_string(),
"-vv".to_string(),
];
let res = parse_args_from_vec(args).expect("should parse args");
assert_eq!(res.config, "myconf.yaml");
assert_eq!(res.dir.as_deref(), Some("/tmp"));
assert_eq!(res.verbose, 2);
assert!(res.verbose > 0);
}
#[test]
fn uses_defaults_when_options_missing() {
let args = vec![
"lazydns".to_string(),
"-c".to_string(),
"cfg.yml".to_string(),
];
let res = parse_args_from_vec(args).expect("should parse");
assert_eq!(res.config, "cfg.yml");
#[cfg(feature = "log")]
assert_eq!(res.verbose, 0);
}
#[test]
fn version_flag_prints_and_exits() {
let args = vec!["lazydns".to_string(), "-V".to_string()];
let res = parse_args_from_vec(args);
assert!(res.is_none());
}
}