use deduper::{find_duplicate_groups, scan_images, DuplicateKind}; use std::env; use std::path::Path; mod review; struct Config { root: String, threshold: u32, review: bool, } fn parse_args() -> Option { let args: Vec = env::args().collect(); let mut root = None; let mut threshold = 8u32; let mut review = false; let mut i = 1; while i < args.len() { match args[i].as_str() { "--review" => review = true, arg if arg.starts_with('-') => { eprintln!("unknown flag: {arg}"); return None; } _ => { if root.is_none() { root = Some(args[i].clone()); } else { threshold = args[i].parse().unwrap_or(8); } } } i += 1; } root.map(|r| Config { root: r, threshold, review }) } fn main() { let config = match parse_args() { Some(c) => c, None => { eprintln!("usage: deduper [hamming-threshold] [--review]"); std::process::exit(1); } }; let root = Path::new(&config.root); let entries = match scan_images(root) { Ok(v) => v, Err(e) => { eprintln!("scan error: {e}"); std::process::exit(1); } }; let groups = find_duplicate_groups(&entries, config.threshold); if groups.is_empty() { println!("no image duplicates found"); return; } if config.review { review::launch_review(&groups); } else { for (idx, group) in groups.iter().enumerate() { let kind = match group.kind { DuplicateKind::Exact => "exact", DuplicateKind::Similar => "similar", }; println!("group {} [{}]", idx + 1, kind); for path in &group.paths { println!(" {}", path.display()); } } } }