diff --git a/src/ai/insight_generator.rs b/src/ai/insight_generator.rs
index 3617261..db3fb20 100644
--- a/src/ai/insight_generator.rs
+++ b/src/ai/insight_generator.rs
@@ -4548,7 +4548,10 @@ mod tests {
#[test]
fn strip_mark_tags_handles_common_patterns() {
- assert_eq!(InsightGenerator::strip_mark_tags("plain text"), "plain text");
+ assert_eq!(
+ InsightGenerator::strip_mark_tags("plain text"),
+ "plain text"
+ );
assert_eq!(
InsightGenerator::strip_mark_tags("…the lake…"),
"…the lake…"
diff --git a/src/database/knowledge_dao.rs b/src/database/knowledge_dao.rs
index 069dd38..06b2b2d 100644
--- a/src/database/knowledge_dao.rs
+++ b/src/database/knowledge_dao.rs
@@ -235,6 +235,7 @@ pub trait KnowledgeDao: Sync + Send {
/// - entity_type: optional, restricts nodes to one type
/// - node_limit: caps the number of nodes; lower-fact-count
/// entities drop first
+ ///
/// Edges between dropped entities are pruned. Persona scoping
/// affects fact_count + edge inclusion (rejected / superseded
/// excluded; All vs Single mirrors the existing pattern).
@@ -937,7 +938,10 @@ impl KnowledgeDao for SqliteKnowledgeDao {
let mut conn = self.connection.lock().expect("KnowledgeDao lock");
let mut q = sql_query(sql).into_boxed();
match persona {
- PersonaFilter::Single { user_id, persona_id } => {
+ PersonaFilter::Single {
+ user_id,
+ persona_id,
+ } => {
q = q
.bind::(*user_id)
.bind::(persona_id.clone());
@@ -977,7 +981,10 @@ impl KnowledgeDao for SqliteKnowledgeDao {
// rows flip — REVIEWED survives so the curator can preserve
// a hand-approved exception under the same predicate.
let touched = match persona {
- PersonaFilter::Single { user_id: uid, persona_id: pid } => diesel::update(
+ PersonaFilter::Single {
+ user_id: uid,
+ persona_id: pid,
+ } => diesel::update(
entity_facts
.filter(predicate.eq(target_predicate))
.filter(user_id.eq(*uid))
@@ -1282,8 +1289,7 @@ impl KnowledgeDao for SqliteKnowledgeDao {
Some(v) => v,
None => continue,
};
- for b in (a + 1)..indices.len() {
- let ib = indices[b];
+ for &ib in &indices[a + 1..] {
let vb = match &decoded[ib] {
Some(v) => v,
None => continue,
diff --git a/src/database/mod.rs b/src/database/mod.rs
index 873b662..4a20702 100644
--- a/src/database/mod.rs
+++ b/src/database/mod.rs
@@ -502,9 +502,9 @@ pub trait ExifDao: Sync + Send {
/// whose calendar position matches the request's span:
/// - `"day"` — same month + day-of-month (any year)
/// - `"week"` — same week-of-year (SQLite `%W`, Monday-anchored —
- /// close to but not exactly ISO week 8601; the
- /// boundary cases at year-start/end can shift by ±1
- /// vs the prior request-time `iso_week()` filter)
+ /// close to but not exactly ISO week 8601; the boundary cases
+ /// at year-start/end can shift by ±1 vs the prior request-time
+ /// `iso_week()` filter)
/// - `"month"` — same month (any year)
///
/// `tz_offset_minutes` is applied to both sides of the strftime
diff --git a/src/database/reconcile.rs b/src/database/reconcile.rs
index 57f69f3..2fade4e 100644
--- a/src/database/reconcile.rs
+++ b/src/database/reconcile.rs
@@ -57,30 +57,28 @@ impl ReconcileStats {
/// watcher tick. Errors are logged but never propagated; reconciliation
/// is best-effort and a transient DB hiccup must not stall the watcher.
pub fn run(conn: &mut SqliteConnection) -> ReconcileStats {
- let mut stats = ReconcileStats::default();
-
- stats.tagged_photo_hashes_filled = match backfill_tagged_photo_hashes(conn) {
- Ok(n) => n,
- Err(e) => {
- warn!("reconcile: tagged_photo hash backfill failed: {:?}", e);
- 0
- }
- };
-
- stats.photo_insights_hashes_filled = match backfill_photo_insights_hashes(conn) {
- Ok(n) => n,
- Err(e) => {
- warn!("reconcile: photo_insights hash backfill failed: {:?}", e);
- 0
- }
- };
-
- stats.photo_insights_demoted = match collapse_insight_currents(conn) {
- Ok(n) => n,
- Err(e) => {
- warn!("reconcile: photo_insights scalar merge failed: {:?}", e);
- 0
- }
+ let stats = ReconcileStats {
+ tagged_photo_hashes_filled: match backfill_tagged_photo_hashes(conn) {
+ Ok(n) => n,
+ Err(e) => {
+ warn!("reconcile: tagged_photo hash backfill failed: {:?}", e);
+ 0
+ }
+ },
+ photo_insights_hashes_filled: match backfill_photo_insights_hashes(conn) {
+ Ok(n) => n,
+ Err(e) => {
+ warn!("reconcile: photo_insights hash backfill failed: {:?}", e);
+ 0
+ }
+ },
+ photo_insights_demoted: match collapse_insight_currents(conn) {
+ Ok(n) => n,
+ Err(e) => {
+ warn!("reconcile: photo_insights scalar merge failed: {:?}", e);
+ 0
+ }
+ },
};
if stats.changed() {
diff --git a/src/faces.rs b/src/faces.rs
index f4bd5cc..ba47508 100644
--- a/src/faces.rs
+++ b/src/faces.rs
@@ -2118,7 +2118,10 @@ async fn update_face_handler(
// the short context string we surface in the response body —
// SQLITE_BUSY here usually means another DAO's writer held the
// lock past `busy_timeout` (5s), which is invisible in `{}`.
- warn!("PATCH /image/faces/{}: 500 — update_face failed: {:#}", id, e);
+ warn!(
+ "PATCH /image/faces/{}: 500 — update_face failed: {:#}",
+ id, e
+ );
return HttpResponse::InternalServerError().body(e.to_string());
}
};
diff --git a/src/handlers/image.rs b/src/handlers/image.rs
index 7266e34..07e977d 100644
--- a/src/handlers/image.rs
+++ b/src/handlers/image.rs
@@ -183,14 +183,15 @@ pub async fn get_image(
// review JPEG, ~1–2 MP). Falls through to NamedFile if no preview is
// available, which preserves the historical behavior for callers
// that genuinely want the original bytes.
- if image_size == PhotoSize::Full && exif::is_tiff_raw(&path) {
- if let Some(preview) = exif::extract_embedded_jpeg_preview(&path) {
- span.set_status(Status::Ok);
- return HttpResponse::Ok()
- .content_type("image/jpeg")
- .insert_header(("Cache-Control", "public, max-age=3600"))
- .body(preview);
- }
+ if image_size == PhotoSize::Full
+ && exif::is_tiff_raw(&path)
+ && let Some(preview) = exif::extract_embedded_jpeg_preview(&path)
+ {
+ span.set_status(Status::Ok);
+ return HttpResponse::Ok()
+ .content_type("image/jpeg")
+ .insert_header(("Cache-Control", "public, max-age=3600"))
+ .body(preview);
}
if let Ok(file) = NamedFile::open(&path) {
@@ -706,7 +707,7 @@ pub async fn set_image_date(
Ok(row) => {
span.set_status(Status::Ok);
HttpResponse::Ok().json(build_metadata_response_for_date_mutation(
- &library,
+ library,
&normalized_path,
row,
))
@@ -757,7 +758,7 @@ pub async fn clear_image_date(
Ok(row) => {
span.set_status(Status::Ok);
HttpResponse::Ok().json(build_metadata_response_for_date_mutation(
- &library,
+ library,
&normalized_path,
row,
))
diff --git a/src/knowledge.rs b/src/knowledge.rs
index 4c3f5a8..66815b2 100644
--- a/src/knowledge.rs
+++ b/src/knowledge.rs
@@ -444,8 +444,7 @@ where
)
.service(web::resource("/graph").route(web::get().to(get_graph::)))
.service(
- web::resource("/predicate-stats")
- .route(web::get().to(get_predicate_stats::)),
+ web::resource("/predicate-stats").route(web::get().to(get_predicate_stats::)),
)
.service(
web::resource("/predicates/{predicate}/bulk-reject")
@@ -1261,12 +1260,8 @@ async fn bulk_reject_predicate(
let persona = resolve_persona_filter(&req, &claims, &persona_dao);
let cx = opentelemetry::Context::current();
let mut dao = dao.lock().expect("Unable to lock KnowledgeDao");
- match dao.bulk_reject_facts_by_predicate(
- &cx,
- &persona,
- &predicate,
- Some(("manual", "manual")),
- ) {
+ match dao.bulk_reject_facts_by_predicate(&cx, &persona, &predicate, Some(("manual", "manual")))
+ {
Ok(rejected) => HttpResponse::Ok().json(BulkRejectResponse { rejected }),
Err(e) => {
log::error!("bulk_reject_predicate error: {:?}", e);
diff --git a/src/libraries.rs b/src/libraries.rs
index 59b614a..6248cfa 100644
--- a/src/libraries.rs
+++ b/src/libraries.rs
@@ -94,7 +94,7 @@ pub fn parse_excluded_dirs_column(raw: Option<&str>) -> Vec {
match raw {
None => Vec::new(),
Some(s) => s
- .split(|c: char| matches!(c, ',' | '\n' | '\r'))
+ .split([',', '\n', '\r'])
.map(str::trim)
.filter(|s| !s.is_empty())
.map(String::from)
@@ -148,10 +148,7 @@ pub fn validate_excluded_dirs_entry(entry: &str) -> Result {
if let Some(rel) = trimmed.strip_prefix('/') {
// Path form. Reject `..` traversal — `base.join(\"../x\")` doesn't
// canonicalise, so `path.starts_with(...)` never matches.
- if rel
- .split('/')
- .any(|seg| seg == "..")
- {
+ if rel.split('/').any(|seg| seg == "..") {
return Err(format!(
"'{}': '..' segments don't normalise — the prefix-match never fires",
trimmed
@@ -542,7 +539,10 @@ pub async fn patch_library(
{
Ok(n) => affected = affected.max(n),
Err(e) => {
- warn!("PATCH /libraries/{}: enabled update failed: {:?}", lib_id, e);
+ warn!(
+ "PATCH /libraries/{}: enabled update failed: {:?}",
+ lib_id, e
+ );
return HttpResponse::InternalServerError().body(format!("{}", e));
}
}
@@ -600,7 +600,9 @@ pub async fn patch_library(
);
HttpResponse::Ok().json(lib)
}
- None => HttpResponse::NotFound().body(format!("library id {} not found after update", lib_id)),
+ None => {
+ HttpResponse::NotFound().body(format!("library id {} not found after update", lib_id))
+ }
}
}
@@ -930,10 +932,7 @@ mod tests {
#[test]
fn validate_strips_trailing_slash_on_path_entries() {
- assert_eq!(
- validate_excluded_dirs_entry("/photos/").unwrap(),
- "/photos"
- );
+ assert_eq!(validate_excluded_dirs_entry("/photos/").unwrap(), "/photos");
assert_eq!(
validate_excluded_dirs_entry("/photos//").unwrap(),
"/photos"