//! Entity repository - trait-based interface //! Avoids sqlx macros requiring DATABASE_URL use anyhow::Result; use async_trait::async_trait; use mem_core::entity::Entity; /// Entity operations trait #[async_trait] pub trait EntityRepoOps: Send + Sync { async fn insert(&self, entity: &Entity) -> Result; async fn find_by_id(&self, id: &str) -> Result>; async fn find_by_name(&self, project_id: &str, name: &str) -> Result>; async fn find_similar_by_name(&self, project_id: &str, embedding: &[f32], threshold: f32, limit: i32) -> Result>; async fn link_source_episode(&self, entity_id: &str, episode_id: i64) -> Result<()>; async fn soft_delete(&self, id: &str) -> Result<()>; async fn record_access(&self, id: &str) -> Result<()>; async fn set_community(&self, entity_id: &str, community_id: &str) -> Result<()>; async fn clear_community(&self, entity_id: &str) -> Result<()>; async fn count_active(&self, project_id: &str) -> Result; } /// Mock implementation for testing (replaces DB access) pub struct MockEntityRepo; #[async_trait] impl EntityRepoOps for MockEntityRepo { async fn insert(&self, entity: &Entity) -> Result { Ok(entity.id.clone()) } async fn find_by_id(&self, _id: &str) -> Result> { Ok(None) } async fn find_by_name(&self, _proj: &str, _name: &str) -> Result> { Ok(None) } async fn find_similar_by_name(&self, _proj: &str, _emb: &[f32], _thresh: f32, _limit: i32) -> Result> { Ok(vec![]) } async fn link_source_episode(&self, _ent: &str, _ep: i64) -> Result<()> { Ok(()) } async fn soft_delete(&self, _id: &str) -> Result<()> { Ok(()) } async fn record_access(&self, _id: &str) -> Result<()> { Ok(()) } async fn set_community(&self, _ent: &str, _com: &str) -> Result<()> { Ok(()) } async fn clear_community(&self, _ent: &str) -> Result<()> { Ok(()) } async fn count_active(&self, _proj: &str) -> Result { Ok(0) } } #[cfg(test)] mod tests { use super::*; #[tokio::test] async fn test_mock_repo() { let repo = MockEntityRepo; assert!(repo.count_active("test").await.is_ok()); } }