From 1d782a6d57f4af886405550178bb40533da3e88d Mon Sep 17 00:00:00 2001 From: LyAhn Date: Fri, 12 Jun 2026 18:36:31 +0100 Subject: [PATCH] style(backend): apply rustfmt across the backend Mechanical cargo fmt pass so the CI rustfmt gate starts green; no semantic changes (verified token-level + cargo check). --- src-tauri/build.rs | 8 +- src-tauri/src/commands.rs | 248 +++++++++++++++++++++++-------------- src-tauri/src/db.rs | 48 ++++--- src-tauri/src/indexer.rs | 59 +++++++-- src-tauri/src/tagger.rs | 6 +- src-tauri/src/thumbnail.rs | 4 +- 6 files changed, 242 insertions(+), 131 deletions(-) diff --git a/src-tauri/build.rs b/src-tauri/build.rs index f6be0ab..48bbd5b 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -15,13 +15,17 @@ fn main() { if let Ok(path) = &cuda_path { println!("cargo:warning=CUDA Toolkit found at {path} — GPU acceleration enabled."); } else { - println!("cargo:warning=CUDA Toolkit (nvcc) found in PATH — GPU acceleration enabled."); + println!( + "cargo:warning=CUDA Toolkit (nvcc) found in PATH — GPU acceleration enabled." + ); } } else { println!("cargo:warning=━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); println!("cargo:warning= candle-cuda feature is enabled but no CUDA Toolkit found."); println!("cargo:warning= Install CUDA Toolkit from https://developer.nvidia.com/cuda-downloads"); - println!("cargo:warning= Or build without GPU support: cargo build --no-default-features"); + println!( + "cargo:warning= Or build without GPU support: cargo build --no-default-features" + ); println!("cargo:warning=━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); } } diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 3451f92..ba33ad0 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -445,7 +445,7 @@ pub async fn find_similar_images( offset, limit + 1, ) - .map_err(|e| e.to_string())?; + .map_err(|e| e.to_string())?; let has_more = matches.len() > limit; let image_ids = matches .into_iter() @@ -499,8 +499,9 @@ pub async fn find_similar_by_region( None => { // Fetch one extra candidate to compensate for the source image that // will be removed, so has_more is accurate and results span multiple pages. - let mut ids = vector::search_image_ids_by_embedding(&conn, &embedding, offset + limit + 2) - .map_err(|e| e.to_string())?; + let mut ids = + vector::search_image_ids_by_embedding(&conn, &embedding, offset + limit + 2) + .map_err(|e| e.to_string())?; ids.retain(|&id| id != params.image_id); ids } @@ -639,7 +640,12 @@ pub async fn search_images_by_tag( offset, ) .map_err(|e| e.to_string())?; - Ok(TagSearchPage { images, total, offset, limit }) + Ok(TagSearchPage { + images, + total, + offset, + limit, + }) } #[tauri::command] @@ -835,7 +841,6 @@ pub struct TagCloudEntry { pub image_ids: Vec, } - /// Clusters the library's image embeddings with k-means and returns one representative /// image per cluster — the member whose embedding is closest to its cluster centroid. /// Results are cached in SQLite keyed by a hash of the embedded image IDs, so repeated @@ -957,7 +962,8 @@ pub async fn get_explore_tags( params: GetExploreTagsParams, ) -> Result, String> { let conn = db.get().map_err(|e| e.to_string())?; - db::get_explore_tags(&conn, params.folder_id, params.limit.unwrap_or(48)).map_err(|e| e.to_string()) + db::get_explore_tags(&conn, params.folder_id, params.limit.unwrap_or(48)) + .map_err(|e| e.to_string()) } #[tauri::command] @@ -969,8 +975,13 @@ pub async fn search_tags_autocomplete( return Ok(vec![]); } let conn = db.get().map_err(|e| e.to_string())?; - db::search_tags_autocomplete(&conn, ¶ms.query, params.folder_id, params.limit.unwrap_or(10)) - .map_err(|e| e.to_string()) + db::search_tags_autocomplete( + &conn, + ¶ms.query, + params.folder_id, + params.limit.unwrap_or(10), + ) + .map_err(|e| e.to_string()) } #[tauri::command] @@ -985,12 +996,15 @@ pub async fn find_duplicates( }; let total = records.len(); - let _ = app.emit("duplicate_scan_progress", DuplicateScanProgress { - phase: "checking".to_string(), - processed: 0, - total, - skipped: 0, - }); + let _ = app.emit( + "duplicate_scan_progress", + DuplicateScanProgress { + phase: "checking".to_string(), + processed: 0, + total, + skipped: 0, + }, + ); // Three-phase detection. // @@ -1007,8 +1021,11 @@ pub async fn find_duplicates( // For sample-hash groups that contain files > 64 KB, compute a full-file // hash to confirm the match before presenting them as deletable duplicates. let app_hash = app.clone(); - let (pairs, skipped_before_confirm, candidate_total): - (Vec<(u64, u64, i64, String)>, usize, usize) = tokio::task::spawn_blocking(move || { + let (pairs, skipped_before_confirm, candidate_total): ( + Vec<(u64, u64, i64, String)>, + usize, + usize, + ) = tokio::task::spawn_blocking(move || { use memmap2::Mmap; use rayon::prelude::*; use std::collections::HashMap; @@ -1016,7 +1033,7 @@ pub async fn find_duplicates( use xxhash_rust::xxh3::{xxh3_64, Xxh3}; const WINDOW: usize = 16 * 1024; // 16 KB per sample window - const N: usize = 4; // windows at 0%, 33%, 66%, 100% + const N: usize = 4; // windows at 0%, 33%, 66%, 100% const COVERED: usize = WINDOW * N; // 64 KB total; also full-coverage threshold // Hash four evenly-spaced 16 KB windows. For files ≤ 64 KB this is @@ -1048,12 +1065,15 @@ pub async fn find_duplicates( }; let done = stat_counter.fetch_add(1, Ordering::Relaxed) + 1; if done % 100 == 0 || done == total { - let _ = app_hash.emit("duplicate_scan_progress", DuplicateScanProgress { - phase: "checking".to_string(), - processed: done, - total, - skipped: stat_skipped.load(Ordering::Relaxed), - }); + let _ = app_hash.emit( + "duplicate_scan_progress", + DuplicateScanProgress { + phase: "checking".to_string(), + processed: done, + total, + skipped: stat_skipped.load(Ordering::Relaxed), + }, + ); } result }) @@ -1076,12 +1096,15 @@ pub async fn find_duplicates( // ── Phase 2: sample hash ────────────────────────────────────────── let c_total = candidates.len(); - let _ = app_hash.emit("duplicate_scan_progress", DuplicateScanProgress { - phase: "hashing".to_string(), - processed: 0, - total: c_total, - skipped: stat_skipped, - }); + let _ = app_hash.emit( + "duplicate_scan_progress", + DuplicateScanProgress { + phase: "hashing".to_string(), + processed: 0, + total: c_total, + skipped: stat_skipped, + }, + ); let counter = AtomicUsize::new(0); let hash_skipped = AtomicUsize::new(0); @@ -1103,12 +1126,15 @@ pub async fn find_duplicates( } let done = counter.fetch_add(1, Ordering::Relaxed) + 1; if done % 100 == 0 || done == c_total { - let _ = app_hash.emit("duplicate_scan_progress", DuplicateScanProgress { - phase: "hashing".to_string(), - processed: done, - total: c_total, - skipped: stat_skipped + hash_skipped.load(Ordering::Relaxed), - }); + let _ = app_hash.emit( + "duplicate_scan_progress", + DuplicateScanProgress { + phase: "hashing".to_string(), + processed: done, + total: c_total, + skipped: stat_skipped + hash_skipped.load(Ordering::Relaxed), + }, + ); } result }) @@ -1126,7 +1152,10 @@ pub async fn find_duplicates( let mut size_hash_map: std::collections::HashMap<(u64, u64), Vec<(i64, String)>> = std::collections::HashMap::new(); for (hash, file_size, id, path) in pairs { - size_hash_map.entry((hash, file_size)).or_default().push((id, path)); + size_hash_map + .entry((hash, file_size)) + .or_default() + .push((id, path)); } // Phase 3: for large-file groups (> 64 KB) the sample hash is not exact; @@ -1140,12 +1169,15 @@ pub async fn find_duplicates( .map(|(_, entries)| entries.len()) .sum(); if confirming_total > 0 { - let _ = app.emit("duplicate_scan_progress", DuplicateScanProgress { - phase: "confirming".to_string(), - processed: 0, - total: confirming_total, - skipped: skipped_before_confirm, - }); + let _ = app.emit( + "duplicate_scan_progress", + DuplicateScanProgress { + phase: "confirming".to_string(), + processed: 0, + total: confirming_total, + skipped: skipped_before_confirm, + }, + ); } let app_confirm = app.clone(); let (confirmed, skipped_files): (Vec<(u64, u64, Vec)>, usize) = @@ -1163,7 +1195,11 @@ pub async fn find_duplicates( .flat_map(|((sample_hash, file_size), entries)| { if file_size <= COVERED { // Sample was already a full hash — one group, no re-read needed. - return vec![(sample_hash, file_size, entries.into_iter().map(|(id, _)| id).collect())]; + return vec![( + sample_hash, + file_size, + entries.into_iter().map(|(id, _)| id).collect(), + )]; } // Full-file hash pass. Group by full hash so that two sets of // files that collide only in the sample produce separate groups. @@ -1179,13 +1215,16 @@ pub async fn find_duplicates( } let done = counter.fetch_add(1, Ordering::Relaxed) + 1; if done % 100 == 0 || done == confirming_total { - let _ = app_confirm.emit("duplicate_scan_progress", DuplicateScanProgress { - phase: "confirming".to_string(), - processed: done, - total: confirming_total, - skipped: skipped_before_confirm - + confirm_skipped.load(Ordering::Relaxed), - }); + let _ = app_confirm.emit( + "duplicate_scan_progress", + DuplicateScanProgress { + phase: "confirming".to_string(), + processed: done, + total: confirming_total, + skipped: skipped_before_confirm + + confirm_skipped.load(Ordering::Relaxed), + }, + ); } (*id, hash) }) @@ -1470,16 +1509,17 @@ pub async fn get_worker_states(folder_ids: Vec) -> Result Result { let conn = db.get().map_err(|e| e.to_string())?; let requested_folder_ids = params.folder_ids.unwrap_or_default(); - let (total, folder_ids) = match (params.folder_id, params.image_id, requested_folder_ids.is_empty()) { + let (total, folder_ids) = match ( + params.folder_id, + params.image_id, + requested_folder_ids.is_empty(), + ) { (_, Some(image_id), _) => { db::enqueue_tagging_job(&conn, image_id).map_err(|e| e.to_string())?; // Look up just this image's folder_id rather than fetching all folders @@ -1675,27 +1719,29 @@ pub async fn clear_tagging_jobs( ) -> Result { let conn = db.get().map_err(|e| e.to_string())?; let requested_folder_ids = params.folder_ids.unwrap_or_default(); - let (n, folder_ids): (usize, Vec) = match (params.folder_id, requested_folder_ids.is_empty()) { - (Some(id), _) => ( - db::clear_tagging_jobs(&conn, Some(id)).map_err(|e| e.to_string())?, - vec![id], - ), - (None, false) => { - let mut total = 0usize; - for &folder_id in &requested_folder_ids { - total += db::clear_tagging_jobs(&conn, Some(folder_id)).map_err(|e| e.to_string())?; + let (n, folder_ids): (usize, Vec) = + match (params.folder_id, requested_folder_ids.is_empty()) { + (Some(id), _) => ( + db::clear_tagging_jobs(&conn, Some(id)).map_err(|e| e.to_string())?, + vec![id], + ), + (None, false) => { + let mut total = 0usize; + for &folder_id in &requested_folder_ids { + total += db::clear_tagging_jobs(&conn, Some(folder_id)) + .map_err(|e| e.to_string())?; + } + (total, requested_folder_ids) } - (total, requested_folder_ids) - } - (None, true) => ( - db::clear_tagging_jobs(&conn, None).map_err(|e| e.to_string())?, - db::get_folders(&conn) - .map_err(|e| e.to_string())? - .into_iter() - .map(|f| f.id) - .collect(), - ), - }; + (None, true) => ( + db::clear_tagging_jobs(&conn, None).map_err(|e| e.to_string())?, + db::get_folders(&conn) + .map_err(|e| e.to_string())? + .into_iter() + .map(|f| f.id) + .collect(), + ), + }; drop(conn); indexer::emit_folder_job_progress(&app, db.inner(), &folder_ids, true); Ok(n) @@ -1747,7 +1793,11 @@ pub async fn get_tagging_queue_scope(app: AppHandle) -> Result { let app_dir = app.path().app_data_dir().map_err(|e| e.to_string())?; let path = app_dir.join(TAGGING_QUEUE_SCOPE_FILE); let value = std::fs::read_to_string(path).unwrap_or_default(); - Ok(if value.trim() == "selected" { "selected".to_string() } else { "all".to_string() }) + Ok(if value.trim() == "selected" { + "selected".to_string() + } else { + "all".to_string() + }) } #[tauri::command] @@ -1760,7 +1810,11 @@ pub async fn set_tagging_queue_scope( if let Some(parent) = path.parent() { std::fs::create_dir_all(parent).map_err(|e| e.to_string())?; } - let value = if params.scope == "selected" { "selected" } else { "all" }; + let value = if params.scope == "selected" { + "selected" + } else { + "all" + }; std::fs::write(path, value).map_err(|e| e.to_string())?; Ok(value.to_string()) } @@ -1825,14 +1879,21 @@ pub struct VacuumResult { } #[tauri::command] -pub async fn get_database_info(app: AppHandle, db: State<'_, DbState>) -> Result { +pub async fn get_database_info( + app: AppHandle, + db: State<'_, DbState>, +) -> Result { let app_dir = app.path().app_data_dir().map_err(|e| e.to_string())?; let db_path = app_dir.join("gallery.db"); let size_bytes = std::fs::metadata(&db_path).map(|m| m.len()).unwrap_or(0); let conn = db.get().map_err(|e| e.to_string())?; - let page_size: i64 = conn.query_row("PRAGMA page_size", [], |r| r.get(0)).map_err(|e| e.to_string())?; - let freelist_count: i64 = conn.query_row("PRAGMA freelist_count", [], |r| r.get(0)).map_err(|e| e.to_string())?; + let page_size: i64 = conn + .query_row("PRAGMA page_size", [], |r| r.get(0)) + .map_err(|e| e.to_string())?; + let freelist_count: i64 = conn + .query_row("PRAGMA freelist_count", [], |r| r.get(0)) + .map_err(|e| e.to_string())?; Ok(DatabaseInfo { size_mb: size_bytes as f64 / 1_048_576.0, @@ -1841,13 +1902,17 @@ pub async fn get_database_info(app: AppHandle, db: State<'_, DbState>) -> Result } #[tauri::command] -pub async fn vacuum_database(app: AppHandle, db: State<'_, DbState>) -> Result { +pub async fn vacuum_database( + app: AppHandle, + db: State<'_, DbState>, +) -> Result { let app_dir = app.path().app_data_dir().map_err(|e| e.to_string())?; let db_path = app_dir.join("gallery.db"); let before_bytes = std::fs::metadata(&db_path).map(|m| m.len()).unwrap_or(0); let conn = db.get().map_err(|e| e.to_string())?; - conn.execute_batch("PRAGMA wal_checkpoint(FULL); VACUUM;").map_err(|e| e.to_string())?; + conn.execute_batch("PRAGMA wal_checkpoint(FULL); VACUUM;") + .map_err(|e| e.to_string())?; drop(conn); let after_bytes = std::fs::metadata(&db_path).map(|m| m.len()).unwrap_or(0); @@ -1870,7 +1935,9 @@ pub struct CleanupOrphanedThumbnailsResult { pub freed_mb: f64, } -fn collect_db_thumbnail_filenames(conn: &rusqlite::Connection) -> Result, String> { +fn collect_db_thumbnail_filenames( + conn: &rusqlite::Connection, +) -> Result, String> { let mut stmt = conn .prepare("SELECT thumbnail_path FROM images WHERE thumbnail_path IS NOT NULL") .map_err(|e| e.to_string())?; @@ -1993,8 +2060,7 @@ pub async fn set_muted_folder_ids( .map(|id| id.to_string()) .collect::>() .join(","); - std::fs::write(settings_dir.join("muted_folder_ids.txt"), content) - .map_err(|e| e.to_string()) + std::fs::write(settings_dir.join("muted_folder_ids.txt"), content).map_err(|e| e.to_string()) } #[tauri::command] diff --git a/src-tauri/src/db.rs b/src-tauri/src/db.rs index 3542e5d..51f5c57 100644 --- a/src-tauri/src/db.rs +++ b/src-tauri/src/db.rs @@ -318,9 +318,7 @@ pub fn migrate(conn: &Connection) -> Result<()> { // Index must be created after ensure_column adds the column; it cannot live // in the execute_batch above because that batch runs before the column exists // on databases that predate Phase 1. - conn.execute_batch( - "CREATE INDEX IF NOT EXISTS idx_images_taken_at ON images(taken_at);", - )?; + conn.execute_batch("CREATE INDEX IF NOT EXISTS idx_images_taken_at ON images(taken_at);")?; ensure_column(conn, "folders", "scan_error", "TEXT")?; vector::migrate(conn)?; @@ -1410,7 +1408,10 @@ pub fn update_image_details( /// Look up the lightweight indexed-media entry for a single path. /// Used by the filesystem watcher to run change-detection before upserting. -pub fn get_indexed_entry_by_path(conn: &Connection, path: &str) -> Result> { +pub fn get_indexed_entry_by_path( + conn: &Connection, + path: &str, +) -> Result> { let result = conn.query_row( "SELECT id, path, modified_at, file_size, media_kind FROM images WHERE path = ?1", [path], @@ -1434,11 +1435,9 @@ pub fn get_indexed_entry_by_path(conn: &Connection, path: &str) -> Result