use clap::Parser; #[derive(clap::ValueEnum, Clone, Debug)] pub enum SamplingFilter { Nearest, Triangle, CatmullRom, Gaussian, Lanczos3, } #[derive(clap::ValueEnum, Clone, Debug)] pub enum ColourSpace { HSL, HSV, HSLUV, LCH, } #[derive(Parser, Clone, Debug)] #[command(author, version, about, long_about = None)] pub struct Args { /// image url or file path #[arg(index = 1)] pub image: String, /// output image width in columns #[arg(short = 'w', long)] pub width: Option, /// output image height in rows #[arg(short = 'H', long)] pub height: Option, /// scaling factors (x:y, e.g., "2:2") #[arg(long, value_parser = parse_xy_pair)] pub scale: Option<(f32, f32)>, /// final aspect ratio (x:y, e.g., "2:1") #[arg(long, value_parser = parse_xy_pair)] pub aspect: Option<(f32, f32)>, /// crop image (x1,y1,x2,y2) #[arg(long, value_parser = parse_crop_coordinates)] pub crop: Option<(u32, u32, u32, u32)>, /// sampling filter #[arg(long, value_enum, default_value_t = SamplingFilter::Nearest)] pub filter: SamplingFilter, /// rotate degrees #[arg(long, default_value_t = 0)] pub rotate: i32, /// flip horizontal #[arg(long, default_value_t = false)] pub fliph: bool, /// flip vertical #[arg(long, default_value_t = false)] pub flipv: bool, /// use IRC99 colours #[arg(long, default_value_t = false, group = "colour", required_unless_present_any = ["ansi", "ansi24"])] pub irc: bool, /// use 8-bit ANSI colours #[arg(long, default_value_t = false, group = "colour", required_unless_present_any = ["irc", "ansi24"])] pub ansi: bool, /// use 24-bit ANSI colours #[arg(long, default_value_t = false, group = "colour", required_unless_present_any = ["irc", "ansi"])] pub ansi24: bool, /// use braille pixels #[arg(long, default_value_t = false, group = "pixel")] pub braille: bool, /// use halfblock pixels #[arg(long, alias = "halfblock", default_value_t = true, group = "pixel")] pub hb: bool, /// use quarterblocks pixels #[arg(long, alias = "quarterblock", default_value_t = false, group = "pixel")] pub qb: bool, /// adjust brightness (0 = no change) #[arg(short = 'b', long, default_value_t = 0.0, allow_hyphen_values = true)] pub brightness: f32, /// adjust contrast (0 = no change) #[arg(short = 'c', long, default_value_t = 0.0, allow_hyphen_values = true)] pub contrast: f32, /// adjust gamma (0 to 255) #[arg(short = 'g', long, default_value_t = 0.0, allow_hyphen_values = true)] pub gamma: f32, /// adjust saturation (0 = no change) #[arg(short = 's', long, default_value_t = 0.0, allow_hyphen_values = true)] pub saturation: f32, /// rotate hue (0 to 360) #[arg(short = 'u', long, default_value_t = 0.0)] pub hue: f32, /// colors are inverted, opposite on the color wheel #[arg(short = 'i', long, default_value_t = false)] pub invert: bool, /// dithering (1 to 8) #[arg(long, short = 'd', long, default_value_t = 0)] pub dither: u32, /// adjust luma brightness (braille only) #[arg(short = 'B', long, default_value_t = 0.0, allow_hyphen_values = true, requires = "braille")] pub luma_brightness: f32, /// adjust luma contrast (braille only) #[arg(short = 'C', long, default_value_t = 0.0, allow_hyphen_values = true, requires = "braille")] pub luma_contrast: f32, /// adjust luma gamma (braille only) #[arg(short = 'G', long, default_value_t = 0.0, allow_hyphen_values = true, requires = "braille")] pub luma_gamma: f32, /// adjust luma saturation (braille only) #[arg(short = 'S', long, default_value_t = 0.0, allow_hyphen_values = true, requires = "braille")] pub luma_saturation: f32, /// luminance is inverted #[arg(short = 'I', long, default_value_t = false, requires = "braille")] pub luma_invert: bool, /// colour space #[arg(long, value_enum, default_value_t = ColourSpace::HSV)] pub colorspace: ColourSpace, /// converts image to black and white #[arg(long, default_value_t = false, group = "grayscale_opts")] pub grayscale: bool, /// exclude grayscale colours from the palette #[arg(long, default_value_t = false, group = "grayscale_opts")] pub nograyscale: bool, /// pixelize pixel size #[arg(long, default_value_t = 0)] pub pixelize: i32, /// simple average of all the neighboring pixels surrounding a given pixel #[arg(long="boxblur", default_value_t = false)] pub box_blur: bool, /// gaussian blur radius #[arg(long="gaussianblur", default_value_t = 0)] pub gaussian_blur: i32, /// oil filter ("[radius],[intensity]") #[arg(long)] pub oil: Option, /// made up of small dots creating a continuous-tone illusion #[arg(long, default_value_t = false)] pub halftone: bool, /// brownish, aged appearance like old photographs #[arg(long, default_value_t = false)] pub sepia: bool, /// adjusts brightness and contrast for better image quality #[arg(long, default_value_t = false)] pub normalize: bool, /// random variations in brightness and color like film grain #[arg(long, default_value_t = false)] pub noise: bool, /// gives a raised, 3d appearance #[arg(long, default_value_t = false)] pub emboss: bool, /// no modifications, unchanged image #[arg(long, default_value_t = false)] pub identity: bool, /// enhances edges and boundaries in an image #[arg(long, default_value_t = false)] pub laplace: bool, /// reduces noise for a cleaner, clearer image #[arg(long="denoise", default_value_t = false)] pub noise_reduction: bool, /// increases clarity and definition, making edges and details more distinct #[arg(long, default_value_t = false)] pub sharpen: bool, /// cool blue tone with increased contrast #[arg(long, default_value_t = false)] pub cali: bool, /// high contrast and vivid colors for a dramatic effect #[arg(long, default_value_t = false)] pub dramatic: bool, /// warm, earthy tones reminiscent of tuscan landscapes #[arg(long, default_value_t = false)] pub firenze: bool, /// warm, golden glow like sunset light #[arg(long, default_value_t = false)] pub golden: bool, /// high-contrast black and white appearance with increased sharpness #[arg(long, default_value_t = false)] pub lix: bool, /// low-fidelity, retro appearance like old photographs or film #[arg(long, default_value_t = false)] pub lofi: bool, /// clean, modern appearance with neutral colors and simple design #[arg(long, default_value_t = false)] pub neue: bool, /// dark, monochromatic appearance with black and gray shades #[arg(long, default_value_t = false)] pub obsidian: bool, /// soft, delicate pink tint like pastel colors #[arg(long="pastelpink", default_value_t = false)] pub pastel_pink: bool, /// bright, high-contrast appearance with vivid colors and sharp details #[arg(long, default_value_t = false)] pub ryo: bool, /// blurred, frosted appearance as if viewed through semi-transparent surface #[arg(long="frostedglass", default_value_t = false)] pub frosted_glass: bool, /// strange, otherworldly appearance with inverted colors and surreal atmosphere #[arg(long, default_value_t = false)] pub solarize: bool, /// highlights edges and boundaries in an image #[arg(long="edgedetection", default_value_t = false)] pub edge_detection: bool, } pub fn parse_args() -> Args { Args::parse() } fn parse_xy_pair(s: &str) -> Result<(f32, f32), String> { let parts: Vec<&str> = s.split(':').collect(); if parts.len() != 2 { return Err(format!( "Invalid format. Expected 'value1:value2', got '{}'", s )); } let first = parts[0].parse::().map_err(|e| { format!( "Failed to parse the first value ('{}') as f32: {}", parts[0], e ) })?; let second = parts[1].parse::().map_err(|e| { format!( "Failed to parse the second value ('{}') as f32: {}", parts[1], e ) })?; if first <= 0.0 || second <= 0.0 { return Err("Both values must be positive numbers.".to_string()); } Ok((first, second)) } fn parse_crop_coordinates(s: &str) -> Result<(u32, u32, u32, u32), String> { let coords: Vec<&str> = s.split(',').collect(); if coords.len() != 4 { return Err(format!( "Invalid crop format '{}'. Expected 'x1,y1,x2,y2' (e.g., '50,50,200,200').", s )); } let x1 = coords[0].parse::().map_err(|_| format!("Invalid x1 value '{}'.", coords[0]))?; let y1 = coords[1].parse::().map_err(|_| format!("Invalid y1 value '{}'.", coords[1]))?; let x2 = coords[2].parse::().map_err(|_| format!("Invalid x2 value '{}'.", coords[2]))?; let y2 = coords[3].parse::().map_err(|_| format!("Invalid y2 value '{}'.", coords[3]))?; Ok((x1, y1, x2, y2)) }