pub use clap::Parser;
use std::path::PathBuf;
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
pub struct Args {
#[arg(short = 'i', long = "input")]
pub input: PathBuf,
#[arg(short = 'o', long = "output")]
pub output: Option<PathBuf>,
#[arg(short = 'r', long = "radius", default_value = "2")]
pub radius: String,
}
impl Args {
pub fn parse_radius(&self) -> Result<Vec<u32>, String> {
let trimmed = self.radius.trim();
if trimmed.contains(',') {
let parts: Vec<&str> = trimmed.split(',').collect();
if parts.len() != 2 {
return Err(format!("Invalid radius format '{}'. Expected single value or 'min,max'", trimmed));
}
let min = parts[0].trim().parse::<u32>().map_err(|_| {
format!("Invalid minimum radius '{}'. Must be a positive integer", parts[0])
})?;
let max = parts[1].trim().parse::<u32>().map_err(|_| {
format!("Invalid maximum radius '{}'. Must be a positive integer", parts[1])
})?;
if min > max {
return Err(format!("Minimum radius {} cannot be greater than maximum radius {}", min, max));
}
if min == 0 || max == 0 {
return Err("Radius must be greater than 0".to_string());
}
Ok((min..=max).collect())
} else {
let radius = trimmed.parse::<u32>().map_err(|_| {
format!("Invalid radius '{}'. Must be a positive integer", trimmed)
})?;
if radius == 0 {
return Err("Radius must be greater than 0".to_string());
}
Ok(vec![radius])
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_single_radius() {
let args = Args {
input: std::path::PathBuf::from("test.jpg"),
output: None,
radius: "3".to_string(),
};
let result = args.parse_radius().unwrap();
assert_eq!(result, vec![3]);
}
#[test]
fn test_parse_radius_range() {
let args = Args {
input: std::path::PathBuf::from("test.jpg"),
output: None,
radius: "2,5".to_string(),
};
let result = args.parse_radius().unwrap();
assert_eq!(result, vec![2, 3, 4, 5]);
}
#[test]
fn test_parse_radius_range_with_spaces() {
let args = Args {
input: std::path::PathBuf::from("test.jpg"),
output: None,
radius: " 1 , 3 ".to_string(),
};
let result = args.parse_radius().unwrap();
assert_eq!(result, vec![1, 2, 3]);
}
#[test]
fn test_parse_radius_invalid_format() {
let args = Args {
input: std::path::PathBuf::from("test.jpg"),
output: None,
radius: "1,2,3".to_string(),
};
assert!(args.parse_radius().is_err());
}
#[test]
fn test_parse_radius_invalid_number() {
let args = Args {
input: std::path::PathBuf::from("test.jpg"),
output: None,
radius: "abc".to_string(),
};
assert!(args.parse_radius().is_err());
}
#[test]
fn test_parse_radius_zero() {
let args = Args {
input: std::path::PathBuf::from("test.jpg"),
output: None,
radius: "0".to_string(),
};
assert!(args.parse_radius().is_err());
}
#[test]
fn test_parse_radius_min_greater_than_max() {
let args = Args {
input: std::path::PathBuf::from("test.jpg"),
output: None,
radius: "5,2".to_string(),
};
assert!(args.parse_radius().is_err());
}
#[test]
fn test_parse_radius_same_min_max() {
let args = Args {
input: std::path::PathBuf::from("test.jpg"),
output: None,
radius: "3,3".to_string(),
};
let result = args.parse_radius().unwrap();
assert_eq!(result, vec![3]);
}
}