Add Cleanup binary for fixing broken DB/file relations
This commit is contained in:
145
src/cleanup/phase1.rs
Normal file
145
src/cleanup/phase1.rs
Normal file
@@ -0,0 +1,145 @@
|
||||
use crate::cleanup::database_updater::DatabaseUpdater;
|
||||
use crate::cleanup::types::{CleanupConfig, CleanupStats};
|
||||
use anyhow::Result;
|
||||
use log::{error, info, warn};
|
||||
use std::path::PathBuf;
|
||||
|
||||
// All supported image extensions to try
|
||||
const SUPPORTED_EXTENSIONS: &[&str] = &[
|
||||
"jpg", "jpeg", "png", "webp", "tiff", "tif", "heif", "heic", "avif", "nef",
|
||||
];
|
||||
|
||||
/// Phase 1: Resolve missing files by searching for alternative extensions
|
||||
pub fn resolve_missing_files(
|
||||
config: &CleanupConfig,
|
||||
db_updater: &mut DatabaseUpdater,
|
||||
) -> Result<CleanupStats> {
|
||||
let mut stats = CleanupStats::new();
|
||||
|
||||
println!("\nPhase 1: Missing File Resolution");
|
||||
println!("---------------------------------");
|
||||
|
||||
// Get all file paths from database
|
||||
println!("Scanning database for file references...");
|
||||
let all_paths = db_updater.get_all_file_paths()?;
|
||||
println!("Found {} unique file paths\n", all_paths.len());
|
||||
|
||||
stats.files_checked = all_paths.len();
|
||||
|
||||
println!("Checking file existence...");
|
||||
let mut missing_count = 0;
|
||||
let mut resolved_count = 0;
|
||||
|
||||
for path_str in all_paths {
|
||||
let full_path = config.base_path.join(&path_str);
|
||||
|
||||
// Check if file exists
|
||||
if full_path.exists() {
|
||||
continue;
|
||||
}
|
||||
|
||||
missing_count += 1;
|
||||
stats.issues_found += 1;
|
||||
|
||||
// Try to find the file with different extensions
|
||||
match find_file_with_alternative_extension(&config.base_path, &path_str) {
|
||||
Some(new_path_str) => {
|
||||
println!(
|
||||
"✓ {} → found as {} {}",
|
||||
path_str,
|
||||
new_path_str,
|
||||
if config.dry_run {
|
||||
"(dry-run, not updated)"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
);
|
||||
|
||||
if !config.dry_run {
|
||||
// Update database
|
||||
match db_updater.update_file_path(&path_str, &new_path_str) {
|
||||
Ok(_) => {
|
||||
resolved_count += 1;
|
||||
stats.issues_fixed += 1;
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to update database for {}: {:?}", path_str, e);
|
||||
stats.add_error(format!("DB update failed for {}: {}", path_str, e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resolved_count += 1;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
warn!("✗ {} → not found with any extension", path_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("\nResults:");
|
||||
println!("- Files checked: {}", stats.files_checked);
|
||||
println!("- Missing files: {}", missing_count);
|
||||
println!("- Resolved: {}", resolved_count);
|
||||
println!(
|
||||
"- Still missing: {}",
|
||||
missing_count - if config.dry_run { 0 } else { resolved_count }
|
||||
);
|
||||
|
||||
if !stats.errors.is_empty() {
|
||||
println!("- Errors: {}", stats.errors.len());
|
||||
}
|
||||
|
||||
Ok(stats)
|
||||
}
|
||||
|
||||
/// Find a file with an alternative extension
|
||||
/// Returns the relative path with the new extension if found
|
||||
fn find_file_with_alternative_extension(base_path: &PathBuf, relative_path: &str) -> Option<String> {
|
||||
let full_path = base_path.join(relative_path);
|
||||
|
||||
// Get the parent directory and file stem (name without extension)
|
||||
let parent = full_path.parent()?;
|
||||
let stem = full_path.file_stem()?.to_str()?;
|
||||
|
||||
// Try each supported extension
|
||||
for ext in SUPPORTED_EXTENSIONS {
|
||||
let test_path = parent.join(format!("{}.{}", stem, ext));
|
||||
if test_path.exists() {
|
||||
// Convert back to relative path
|
||||
if let Ok(rel) = test_path.strip_prefix(base_path) {
|
||||
if let Some(rel_str) = rel.to_str() {
|
||||
return Some(rel_str.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs;
|
||||
use tempfile::TempDir;
|
||||
|
||||
#[test]
|
||||
fn test_find_file_with_alternative_extension() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let base_path = temp_dir.path().to_path_buf();
|
||||
|
||||
// Create a test file with .jpeg extension
|
||||
let test_file = base_path.join("test.jpeg");
|
||||
fs::write(&test_file, b"test").unwrap();
|
||||
|
||||
// Try to find it as .jpg
|
||||
let result = find_file_with_alternative_extension(&base_path, "test.jpg");
|
||||
assert!(result.is_some());
|
||||
assert_eq!(result.unwrap(), "test.jpeg");
|
||||
|
||||
// Try to find non-existent file
|
||||
let result = find_file_with_alternative_extension(&base_path, "nonexistent.jpg");
|
||||
assert!(result.is_none());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user