From 387ce23afdfeca184a198f139e9343c1bbab7498 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 18 Mar 2026 16:59:39 -0400 Subject: [PATCH] feat: add tag_dao to InsightGenerator for tag-based context enrichment Threads SqliteTagDao through InsightGenerator and AppState (both default and test_state). Adds Send+Sync bounds to TagDao trait with unsafe impls for SqliteTagDao (always Mutex-protected) and TestTagDao (single-threaded). Co-Authored-By: Claude Sonnet 4.6 --- src/ai/insight_generator.rs | 4 ++++ src/state.rs | 7 +++++++ src/tags.rs | 10 +++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/ai/insight_generator.rs b/src/ai/insight_generator.rs index 77ac02a..534c8aa 100644 --- a/src/ai/insight_generator.rs +++ b/src/ai/insight_generator.rs @@ -15,6 +15,7 @@ use crate::database::models::InsertPhotoInsight; use crate::database::{ CalendarEventDao, DailySummaryDao, ExifDao, InsightDao, LocationHistoryDao, SearchHistoryDao, }; +use crate::tags::TagDao; use crate::memories::extract_date_from_filename; use crate::otel::global_tracer; use crate::utils::normalize_path; @@ -45,6 +46,7 @@ pub struct InsightGenerator { calendar_dao: Arc>>, location_dao: Arc>>, search_dao: Arc>>, + tag_dao: Arc>>, base_path: String, } @@ -59,6 +61,7 @@ impl InsightGenerator { calendar_dao: Arc>>, location_dao: Arc>>, search_dao: Arc>>, + tag_dao: Arc>>, base_path: String, ) -> Self { Self { @@ -70,6 +73,7 @@ impl InsightGenerator { calendar_dao, location_dao, search_dao, + tag_dao, base_path, } } diff --git a/src/state.rs b/src/state.rs index 2bf0e7e..578ea78 100644 --- a/src/state.rs +++ b/src/state.rs @@ -5,6 +5,7 @@ use crate::database::{ SqliteLocationHistoryDao, SqliteSearchHistoryDao, }; use crate::database::{PreviewDao, SqlitePreviewDao}; +use crate::tags::{SqliteTagDao, TagDao}; use crate::video::actors::{ PlaylistGenerator, PreviewClipGenerator, StreamActor, VideoPlaylistManager, }; @@ -119,6 +120,8 @@ impl Default for AppState { Arc::new(Mutex::new(Box::new(SqliteLocationHistoryDao::new()))); let search_dao: Arc>> = Arc::new(Mutex::new(Box::new(SqliteSearchHistoryDao::new()))); + let tag_dao: Arc>> = + Arc::new(Mutex::new(Box::new(SqliteTagDao::default()))); // Load base path let base_path = env::var("BASE_PATH").expect("BASE_PATH was not set in the env"); @@ -133,6 +136,7 @@ impl Default for AppState { calendar_dao.clone(), location_dao.clone(), search_dao.clone(), + tag_dao.clone(), base_path.clone(), ); @@ -196,6 +200,8 @@ impl AppState { Arc::new(Mutex::new(Box::new(SqliteLocationHistoryDao::new()))); let search_dao: Arc>> = Arc::new(Mutex::new(Box::new(SqliteSearchHistoryDao::new()))); + let tag_dao: Arc>> = + Arc::new(Mutex::new(Box::new(SqliteTagDao::default()))); // Initialize test InsightGenerator with all data sources let base_path_str = base_path.to_string_lossy().to_string(); @@ -208,6 +214,7 @@ impl AppState { calendar_dao.clone(), location_dao.clone(), search_dao.clone(), + tag_dao.clone(), base_path_str.clone(), ); diff --git a/src/tags.rs b/src/tags.rs index 3a16c2f..360d52b 100644 --- a/src/tags.rs +++ b/src/tags.rs @@ -276,7 +276,7 @@ pub struct AddTagsRequest { pub tag_ids: Vec, } -pub trait TagDao { +pub trait TagDao: Send + Sync { fn get_all_tags( &mut self, context: &opentelemetry::Context, @@ -345,6 +345,10 @@ impl Default for SqliteTagDao { } } +// SAFETY: SqliteTagDao is always accessed through Arc>, +// so concurrent access is prevented by the Mutex. +unsafe impl Sync for SqliteTagDao {} + impl TagDao for SqliteTagDao { fn get_all_tags( &mut self, @@ -735,6 +739,10 @@ mod tests { } } + // SAFETY: TestTagDao is only used in single-threaded tests + unsafe impl Send for TestTagDao {} + unsafe impl Sync for TestTagDao {} + impl TagDao for TestTagDao { fn get_all_tags( &mut self,