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 <noreply@anthropic.com>
This commit is contained in:
Cameron
2026-03-18 16:59:39 -04:00
parent b31b4b903c
commit 387ce23afd
3 changed files with 20 additions and 1 deletions

View File

@@ -15,6 +15,7 @@ use crate::database::models::InsertPhotoInsight;
use crate::database::{ use crate::database::{
CalendarEventDao, DailySummaryDao, ExifDao, InsightDao, LocationHistoryDao, SearchHistoryDao, CalendarEventDao, DailySummaryDao, ExifDao, InsightDao, LocationHistoryDao, SearchHistoryDao,
}; };
use crate::tags::TagDao;
use crate::memories::extract_date_from_filename; use crate::memories::extract_date_from_filename;
use crate::otel::global_tracer; use crate::otel::global_tracer;
use crate::utils::normalize_path; use crate::utils::normalize_path;
@@ -45,6 +46,7 @@ pub struct InsightGenerator {
calendar_dao: Arc<Mutex<Box<dyn CalendarEventDao>>>, calendar_dao: Arc<Mutex<Box<dyn CalendarEventDao>>>,
location_dao: Arc<Mutex<Box<dyn LocationHistoryDao>>>, location_dao: Arc<Mutex<Box<dyn LocationHistoryDao>>>,
search_dao: Arc<Mutex<Box<dyn SearchHistoryDao>>>, search_dao: Arc<Mutex<Box<dyn SearchHistoryDao>>>,
tag_dao: Arc<Mutex<Box<dyn TagDao>>>,
base_path: String, base_path: String,
} }
@@ -59,6 +61,7 @@ impl InsightGenerator {
calendar_dao: Arc<Mutex<Box<dyn CalendarEventDao>>>, calendar_dao: Arc<Mutex<Box<dyn CalendarEventDao>>>,
location_dao: Arc<Mutex<Box<dyn LocationHistoryDao>>>, location_dao: Arc<Mutex<Box<dyn LocationHistoryDao>>>,
search_dao: Arc<Mutex<Box<dyn SearchHistoryDao>>>, search_dao: Arc<Mutex<Box<dyn SearchHistoryDao>>>,
tag_dao: Arc<Mutex<Box<dyn TagDao>>>,
base_path: String, base_path: String,
) -> Self { ) -> Self {
Self { Self {
@@ -70,6 +73,7 @@ impl InsightGenerator {
calendar_dao, calendar_dao,
location_dao, location_dao,
search_dao, search_dao,
tag_dao,
base_path, base_path,
} }
} }

View File

@@ -5,6 +5,7 @@ use crate::database::{
SqliteLocationHistoryDao, SqliteSearchHistoryDao, SqliteLocationHistoryDao, SqliteSearchHistoryDao,
}; };
use crate::database::{PreviewDao, SqlitePreviewDao}; use crate::database::{PreviewDao, SqlitePreviewDao};
use crate::tags::{SqliteTagDao, TagDao};
use crate::video::actors::{ use crate::video::actors::{
PlaylistGenerator, PreviewClipGenerator, StreamActor, VideoPlaylistManager, PlaylistGenerator, PreviewClipGenerator, StreamActor, VideoPlaylistManager,
}; };
@@ -119,6 +120,8 @@ impl Default for AppState {
Arc::new(Mutex::new(Box::new(SqliteLocationHistoryDao::new()))); Arc::new(Mutex::new(Box::new(SqliteLocationHistoryDao::new())));
let search_dao: Arc<Mutex<Box<dyn SearchHistoryDao>>> = let search_dao: Arc<Mutex<Box<dyn SearchHistoryDao>>> =
Arc::new(Mutex::new(Box::new(SqliteSearchHistoryDao::new()))); Arc::new(Mutex::new(Box::new(SqliteSearchHistoryDao::new())));
let tag_dao: Arc<Mutex<Box<dyn TagDao>>> =
Arc::new(Mutex::new(Box::new(SqliteTagDao::default())));
// Load base path // Load base path
let base_path = env::var("BASE_PATH").expect("BASE_PATH was not set in the env"); 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(), calendar_dao.clone(),
location_dao.clone(), location_dao.clone(),
search_dao.clone(), search_dao.clone(),
tag_dao.clone(),
base_path.clone(), base_path.clone(),
); );
@@ -196,6 +200,8 @@ impl AppState {
Arc::new(Mutex::new(Box::new(SqliteLocationHistoryDao::new()))); Arc::new(Mutex::new(Box::new(SqliteLocationHistoryDao::new())));
let search_dao: Arc<Mutex<Box<dyn SearchHistoryDao>>> = let search_dao: Arc<Mutex<Box<dyn SearchHistoryDao>>> =
Arc::new(Mutex::new(Box::new(SqliteSearchHistoryDao::new()))); Arc::new(Mutex::new(Box::new(SqliteSearchHistoryDao::new())));
let tag_dao: Arc<Mutex<Box<dyn TagDao>>> =
Arc::new(Mutex::new(Box::new(SqliteTagDao::default())));
// Initialize test InsightGenerator with all data sources // Initialize test InsightGenerator with all data sources
let base_path_str = base_path.to_string_lossy().to_string(); let base_path_str = base_path.to_string_lossy().to_string();
@@ -208,6 +214,7 @@ impl AppState {
calendar_dao.clone(), calendar_dao.clone(),
location_dao.clone(), location_dao.clone(),
search_dao.clone(), search_dao.clone(),
tag_dao.clone(),
base_path_str.clone(), base_path_str.clone(),
); );

View File

@@ -276,7 +276,7 @@ pub struct AddTagsRequest {
pub tag_ids: Vec<i32>, pub tag_ids: Vec<i32>,
} }
pub trait TagDao { pub trait TagDao: Send + Sync {
fn get_all_tags( fn get_all_tags(
&mut self, &mut self,
context: &opentelemetry::Context, context: &opentelemetry::Context,
@@ -345,6 +345,10 @@ impl Default for SqliteTagDao {
} }
} }
// SAFETY: SqliteTagDao is always accessed through Arc<Mutex<...>>,
// so concurrent access is prevented by the Mutex.
unsafe impl Sync for SqliteTagDao {}
impl TagDao for SqliteTagDao { impl TagDao for SqliteTagDao {
fn get_all_tags( fn get_all_tags(
&mut self, &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 { impl TagDao for TestTagDao {
fn get_all_tags( fn get_all_tags(
&mut self, &mut self,