From 449ce1fda186a8d3c093cd3b23d1e80950f63223 Mon Sep 17 00:00:00 2001 From: Cameron Cordes Date: Wed, 27 May 2026 13:13:48 -0400 Subject: [PATCH] chore: resolve all clippy warnings and formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace impl ToString with impl Display for InsightJobStatus and InsightGenerationType - Rename from_str → parse to avoid confusion with std::str::FromStr - Collapse nested if statements (handlers, insight_chat, insight_generator, image handlers) - Use is_multiple_of() instead of manual modulo checks - Suppress deprecated diesel::dsl::count_distinct (no drop-in replacement available in current Diesel version) - Scope MutexGuard in synthesize_merge to drop before await - Allow dead_code on generate_no_think, enumerate_indexable_files, total_deleted (intended for future use) - Allow type_complexity on Diesel query result tuples Co-Authored-By: Claude Opus 4.7 (1M context) --- src/ai/handlers.rs | 11 ++++--- src/ai/insight_chat.rs | 8 ++--- src/ai/insight_generator.rs | 8 ++--- src/ai/ollama.rs | 5 +--- src/bin/probe_clip_search.rs | 2 +- src/clip_search.rs | 2 +- src/database/mod.rs | 2 ++ src/database/models.rs | 32 +++++++------------- src/faces.rs | 44 +++++++++++++++++++-------- src/file_scan.rs | 1 + src/handlers/image.rs | 36 +++++++++++----------- src/knowledge.rs | 58 +++++++++++++++++------------------- src/library_maintenance.rs | 1 + 13 files changed, 108 insertions(+), 102 deletions(-) diff --git a/src/ai/handlers.rs b/src/ai/handlers.rs index 57f9df6..c902432 100644 --- a/src/ai/handlers.rs +++ b/src/ai/handlers.rs @@ -97,7 +97,7 @@ pub async fn generation_status_handler( Ok(Some(job)) => { return HttpResponse::Ok().json(GenerationStatusResponse { job_id: job.id, - status: InsightJobStatus::from_str(&job.status), + status: InsightJobStatus::parse(&job.status), started_at: job.started_at, completed_at: job.completed_at, result_insight_id: job.result_insight_id, @@ -133,7 +133,7 @@ pub async fn generation_status_handler( Ok(Some(job)) => { return HttpResponse::Ok().json(GenerationStatusResponse { job_id: job.id, - status: InsightJobStatus::from_str(&job.status), + status: InsightJobStatus::parse(&job.status), started_at: job.started_at, completed_at: job.completed_at, result_insight_id: job.result_insight_id, @@ -472,10 +472,9 @@ pub async fn generate_insight_handler( if let Err(e) = dao.complete_job(&ctx, job_id, id) { log::error!("Failed to mark job {} as completed: {:?}", job_id, e); } - } else { - if let Err(e) = dao.fail_job(&ctx, job_id, "generation returned no insight") { - log::error!("Failed to mark job {} as failed: {:?}", job_id, e); - } + } else if let Err(e) = dao.fail_job(&ctx, job_id, "generation returned no insight") + { + log::error!("Failed to mark job {} as failed: {:?}", job_id, e); } } Ok(Ok(Err(e))) => { diff --git a/src/ai/insight_chat.rs b/src/ai/insight_chat.rs index d5f2fd9..2131efc 100644 --- a/src/ai/insight_chat.rs +++ b/src/ai/insight_chat.rs @@ -1022,10 +1022,10 @@ impl InsightChatService { ); let system_msg = ChatMessage::system(system_content); let mut user_msg = ChatMessage::user(req.user_message.clone()); - if backend.images_inline { - if let Some(ref img) = image_base64 { - user_msg.images = Some(vec![img.clone()]); - } + if backend.images_inline + && let Some(ref img) = image_base64 + { + user_msg.images = Some(vec![img.clone()]); } let mut messages = vec![system_msg, user_msg]; diff --git a/src/ai/insight_generator.rs b/src/ai/insight_generator.rs index 59ccaad..d5542dc 100644 --- a/src/ai/insight_generator.rs +++ b/src/ai/insight_generator.rs @@ -4039,10 +4039,10 @@ Return ONLY the summary, nothing else."#, // user message; describe-then-inline → text was already injected. let system_msg = ChatMessage::system(system_content); let mut user_msg = ChatMessage::user(user_content); - if backend.images_inline { - if let Some(ref img) = image_base64 { - user_msg.images = Some(vec![img.clone()]); - } + if backend.images_inline + && let Some(ref img) = image_base64 + { + user_msg.images = Some(vec![img.clone()]); } let mut messages = vec![system_msg, user_msg]; diff --git a/src/ai/ollama.rs b/src/ai/ollama.rs index c56e1e7..680668f 100644 --- a/src/ai/ollama.rs +++ b/src/ai/ollama.rs @@ -424,10 +424,7 @@ impl OllamaClient { self.generate_with_images(prompt, system, None).await } - /// Variant of `generate` that sets Ollama's top-level `think: false`. - /// Used by latency-sensitive callers like the rerank pass, where the - /// task has nothing to reason about and chain-of-thought tokens are - /// wasted wall time. Server-side no-op on non-reasoning models. + #[allow(dead_code)] pub async fn generate_no_think(&self, prompt: &str, system: Option<&str>) -> Result { self.generate_with_options(prompt, system, None, Some(false)) .await diff --git a/src/bin/probe_clip_search.rs b/src/bin/probe_clip_search.rs index 80d5e7f..c9e652f 100644 --- a/src/bin/probe_clip_search.rs +++ b/src/bin/probe_clip_search.rs @@ -219,7 +219,7 @@ async fn main() -> anyhow::Result<()> { } let sim = dot(&vec, &query_vec); scores.push((sim, rel_path.clone())); - if encoded % 10 == 0 { + if encoded.is_multiple_of(10) { info!( "progress: {} encoded, {:.1}s elapsed", encoded, diff --git a/src/clip_search.rs b/src/clip_search.rs index d91e490..98ea96e 100644 --- a/src/clip_search.rs +++ b/src/clip_search.rs @@ -109,7 +109,7 @@ struct SearchError { /// `None` on malformed bytes — those rows get skipped rather than /// failing the whole query. fn decode_embedding(bytes: &[u8]) -> Option> { - if bytes.is_empty() || bytes.len() % 4 != 0 { + if bytes.is_empty() || !bytes.len().is_multiple_of(4) { return None; } let mut out = Vec::with_capacity(bytes.len() / 4); diff --git a/src/database/mod.rs b/src/database/mod.rs index 5aa160d..3969712 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -2103,6 +2103,7 @@ impl ExifDao for SqliteExifDao { .map_err(|_| DbError::new(DbErrorKind::UpdateError)) } + #[allow(clippy::type_complexity)] fn list_duplicates_exact( &mut self, context: &opentelemetry::Context, @@ -2198,6 +2199,7 @@ impl ExifDao for SqliteExifDao { .map_err(|_| DbError::new(DbErrorKind::QueryError)) } + #[allow(clippy::type_complexity)] fn list_perceptual_candidates( &mut self, context: &opentelemetry::Context, diff --git a/src/database/models.rs b/src/database/models.rs index d670844..75e4e60 100644 --- a/src/database/models.rs +++ b/src/database/models.rs @@ -23,16 +23,8 @@ impl InsightJobStatus { Self::Cancelled => "cancelled", } } -} -impl ToString for InsightJobStatus { - fn to_string(&self) -> String { - self.as_str().to_string() - } -} - -impl InsightJobStatus { - pub fn from_str(s: &str) -> Self { + pub fn parse(s: &str) -> Self { match s { "running" => Self::Running, "completed" => Self::Completed, @@ -49,6 +41,12 @@ impl InsightJobStatus { } } +impl std::fmt::Display for InsightJobStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.as_str()) + } +} + /// Type of insight generation (standard vs agentic). #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] #[serde(rename_all = "snake_case")] @@ -66,19 +64,9 @@ impl InsightGenerationType { } } -impl ToString for InsightGenerationType { - fn to_string(&self) -> String { - self.as_str().to_string() - } -} - -impl InsightGenerationType { - pub fn from_str(s: &str) -> Self { - match s { - "standard" => Self::Standard, - "agentic" => Self::Agentic, - _ => Self::Standard, - } +impl std::fmt::Display for InsightGenerationType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.as_str()) } } diff --git a/src/faces.rs b/src/faces.rs index ba47508..3288aa3 100644 --- a/src/faces.rs +++ b/src/faces.rs @@ -1024,9 +1024,14 @@ impl FaceDao for SqliteFaceDao { if let Some(lib) = library_id { q = q.filter(face_detections::library_id.eq(lib)); } - q.select(diesel::dsl::count_distinct(face_detections::content_hash)) - .first(conn.deref_mut()) - .with_context(|| "stats: scanned")? + q.select( + #[allow(deprecated)] + { + diesel::dsl::count_distinct(face_detections::content_hash) + }, + ) + .first(conn.deref_mut()) + .with_context(|| "stats: scanned")? }; let with_faces: i64 = { let mut q = face_detections::table @@ -1035,9 +1040,14 @@ impl FaceDao for SqliteFaceDao { if let Some(lib) = library_id { q = q.filter(face_detections::library_id.eq(lib)); } - q.select(diesel::dsl::count_distinct(face_detections::content_hash)) - .first(conn.deref_mut()) - .with_context(|| "stats: with_faces")? + q.select( + #[allow(deprecated)] + { + diesel::dsl::count_distinct(face_detections::content_hash) + }, + ) + .first(conn.deref_mut()) + .with_context(|| "stats: with_faces")? }; let no_faces: i64 = { let mut q = face_detections::table @@ -1046,9 +1056,14 @@ impl FaceDao for SqliteFaceDao { if let Some(lib) = library_id { q = q.filter(face_detections::library_id.eq(lib)); } - q.select(diesel::dsl::count_distinct(face_detections::content_hash)) - .first(conn.deref_mut()) - .with_context(|| "stats: no_faces")? + q.select( + #[allow(deprecated)] + { + diesel::dsl::count_distinct(face_detections::content_hash) + }, + ) + .first(conn.deref_mut()) + .with_context(|| "stats: no_faces")? }; let failed: i64 = { let mut q = face_detections::table @@ -1057,9 +1072,14 @@ impl FaceDao for SqliteFaceDao { if let Some(lib) = library_id { q = q.filter(face_detections::library_id.eq(lib)); } - q.select(diesel::dsl::count_distinct(face_detections::content_hash)) - .first(conn.deref_mut()) - .with_context(|| "stats: failed")? + q.select( + #[allow(deprecated)] + { + diesel::dsl::count_distinct(face_detections::content_hash) + }, + ) + .first(conn.deref_mut()) + .with_context(|| "stats: failed")? }; // Image-extension filter mirrors `list_unscanned_candidates` so // SCANNED can actually reach 100%: videos sit in `image_exif` but diff --git a/src/file_scan.rs b/src/file_scan.rs index 9e6e79d..51318f8 100644 --- a/src/file_scan.rs +++ b/src/file_scan.rs @@ -53,6 +53,7 @@ pub fn walk_library_files(base_path: &Path, excluded_dirs: &[String]) -> Vec( .json(serde_json::json!({"error": "source_id and target_id must differ"})); } - let cx = opentelemetry::Context::current(); - let mut dao = dao.lock().expect("Unable to lock KnowledgeDao"); + let (source, target) = { + let cx = opentelemetry::Context::current(); + let mut dao = dao.lock().expect("Unable to lock KnowledgeDao"); - let source = match dao.get_entity_by_id(&cx, body.source_id) { - Ok(Some(e)) => e, - Ok(None) => { - return HttpResponse::BadRequest() - .json(serde_json::json!({"error": "source entity not found"})); - } - Err(e) => { - log::error!("synthesize_merge source lookup: {:?}", e); - return HttpResponse::InternalServerError() - .json(serde_json::json!({"error": "Database error"})); - } + let source = match dao.get_entity_by_id(&cx, body.source_id) { + Ok(Some(e)) => e, + Ok(None) => { + return HttpResponse::BadRequest() + .json(serde_json::json!({"error": "source entity not found"})); + } + Err(e) => { + log::error!("synthesize_merge source lookup: {:?}", e); + return HttpResponse::InternalServerError() + .json(serde_json::json!({"error": "Database error"})); + } + }; + let target = match dao.get_entity_by_id(&cx, body.target_id) { + Ok(Some(e)) => e, + Ok(None) => { + return HttpResponse::BadRequest() + .json(serde_json::json!({"error": "target entity not found"})); + } + Err(e) => { + log::error!("synthesize_merge target lookup: {:?}", e); + return HttpResponse::InternalServerError() + .json(serde_json::json!({"error": "Database error"})); + } + }; + (source, target) }; - let target = match dao.get_entity_by_id(&cx, body.target_id) { - Ok(Some(e)) => e, - Ok(None) => { - return HttpResponse::BadRequest() - .json(serde_json::json!({"error": "target entity not found"})); - } - Err(e) => { - log::error!("synthesize_merge target lookup: {:?}", e); - return HttpResponse::InternalServerError() - .json(serde_json::json!({"error": "Database error"})); - } - }; - - // Drop the DAO lock before the LLM call — the generate request - // is the slow part (seconds) and we don't want to block other - // knowledge reads while it runs. - drop(dao); let source_desc = if source.description.trim().is_empty() { "(none)".to_string() diff --git a/src/library_maintenance.rs b/src/library_maintenance.rs index 67f4ba2..ffa87bb 100644 --- a/src/library_maintenance.rs +++ b/src/library_maintenance.rs @@ -296,6 +296,7 @@ impl GcStats { || self.revived > 0 } + #[allow(dead_code)] pub fn total_deleted(&self) -> usize { self.deleted_face_detections + self.deleted_tagged_photo + self.deleted_photo_insights }