feat: complete image phase - SHA-256 exact + dHash perceptual duplicate detection

- lib.rs: scan_images, compute_dhash, hamming, find_duplicate_groups
- main.rs: CLI with folder arg and optional hamming threshold
- 13 unit tests: hamming, is_image_path, dhash, find_duplicate_groups
- 7 integration tests: real files, empty dir, cropped, non-image exclusion,
  subdirectory recursion, single file, CLI binary output
- All 20 tests passing
This commit is contained in:
admin
2026-04-27 23:33:27 +00:00
parent 71c8df2de5
commit e1f8201b5c
6 changed files with 1556 additions and 1 deletions

View File

@@ -1,3 +1,42 @@
use deduper::{find_duplicate_groups, scan_images, DuplicateKind};
use std::env;
use std::path::Path;
fn main() {
println!("Hello, world!");
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
eprintln!("usage: deduper <folder> [hamming-threshold]");
std::process::exit(1);
}
let root = Path::new(&args[1]);
let threshold = args
.get(2)
.and_then(|s| s.parse::<u32>().ok())
.unwrap_or(8);
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, threshold);
if groups.is_empty() {
println!("no image duplicates found");
return;
}
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());
}
}
}