1use crate::error::Result;
2use crate::traits::{CacheStore, RelationalStore, SearchStore, VectorStore};
3use std::sync::Arc;
4
5pub struct StoreRouter {
6 relational: Arc<dyn RelationalStore>,
7 vector: Arc<dyn VectorStore>,
8 cache: Arc<dyn CacheStore>,
9 search: Arc<dyn SearchStore>,
10}
11
12impl StoreRouter {
13 pub fn new(
14 relational: Arc<dyn RelationalStore>,
15 vector: Arc<dyn VectorStore>,
16 cache: Arc<dyn CacheStore>,
17 search: Arc<dyn SearchStore>,
18 ) -> Self {
19 Self {
20 relational,
21 vector,
22 cache,
23 search,
24 }
25 }
26
27 pub fn relational(&self) -> &dyn RelationalStore {
28 self.relational.as_ref()
29 }
30
31 pub fn vector(&self) -> &dyn VectorStore {
32 self.vector.as_ref()
33 }
34
35 pub fn cache(&self) -> &dyn CacheStore {
36 self.cache.as_ref()
37 }
38
39 pub fn search(&self) -> &dyn SearchStore {
40 self.search.as_ref()
41 }
42
43 pub async fn health_check(&self) -> StoreHealth {
44 let relational_ok = self.relational.ping().await.is_ok();
45
46 StoreHealth {
47 relational: relational_ok,
48 vector: true,
49 cache: true,
50 search: true,
51 }
52 }
53}
54
55#[derive(Debug, Clone)]
56pub struct StoreHealth {
57 pub relational: bool,
58 pub vector: bool,
59 pub cache: bool,
60 pub search: bool,
61}
62
63impl StoreHealth {
64 pub fn is_healthy(&self) -> bool {
65 self.relational && self.vector && self.cache && self.search
66 }
67
68 pub fn unhealthy_backends(&self) -> Vec<&'static str> {
69 let mut unhealthy = Vec::new();
70 if !self.relational {
71 unhealthy.push("relational");
72 }
73 if !self.vector {
74 unhealthy.push("vector");
75 }
76 if !self.cache {
77 unhealthy.push("cache");
78 }
79 if !self.search {
80 unhealthy.push("search");
81 }
82 unhealthy
83 }
84}
85
86#[cfg(all(feature = "sqlite", feature = "memory-cache"))]
87impl StoreRouter {
88 pub fn local_default() -> Result<Self> {
89 use crate::local::{MemoryCache, MemoryVectorStore, SqliteFtsStore, SqliteStore};
90
91 let sqlite = Arc::new(SqliteStore::memory()?);
92 let vector = Arc::new(MemoryVectorStore::new());
93 let cache = Arc::new(MemoryCache::new(10000));
94 let search = Arc::new(SqliteFtsStore::new(Arc::clone(&sqlite)));
95
96 Ok(Self {
97 relational: sqlite,
98 vector,
99 cache,
100 search,
101 })
102 }
103
104 pub fn local_persistent(data_dir: &str) -> Result<Self> {
105 use crate::local::{MemoryCache, MemoryVectorStore, SqliteFtsStore, SqliteStore};
106
107 std::fs::create_dir_all(data_dir)
108 .map_err(|e| crate::error::StoreError::connection(e.to_string()))?;
109
110 let db_path = format!("{}/hehe.db", data_dir);
111 let sqlite = Arc::new(SqliteStore::open(&db_path)?);
112 let vector = Arc::new(MemoryVectorStore::new());
113 let cache = Arc::new(MemoryCache::new(10000));
114 let search = Arc::new(SqliteFtsStore::new(Arc::clone(&sqlite)));
115
116 Ok(Self {
117 relational: sqlite,
118 vector,
119 cache,
120 search,
121 })
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[cfg(all(feature = "sqlite", feature = "memory-cache"))]
130 #[tokio::test]
131 async fn test_store_router_creation() {
132 let router = StoreRouter::local_default().unwrap();
133
134 assert_eq!(router.relational().backend_name(), "sqlite");
135 assert_eq!(router.vector().backend_name(), "memory");
136 assert_eq!(router.cache().backend_name(), "memory");
137 assert_eq!(router.search().backend_name(), "sqlite-fts5");
138 }
139
140 #[cfg(all(feature = "sqlite", feature = "memory-cache"))]
141 #[tokio::test]
142 async fn test_store_router_health_check() {
143 let router = StoreRouter::local_default().unwrap();
144 let health = router.health_check().await;
145
146 assert!(health.is_healthy());
147 assert!(health.unhealthy_backends().is_empty());
148 }
149}