project_rag/relations/storage/
lance_store.rs1use anyhow::{Context, Result};
4use async_trait::async_trait;
5use std::path::PathBuf;
6use std::sync::Arc;
7use tokio::sync::RwLock;
8
9use super::{RelationsStats, RelationsStore};
10use crate::relations::types::{CallEdge, Definition, Reference};
11
12pub struct LanceRelationsStore {
16 db_path: PathBuf,
18 db: Arc<RwLock<Option<lancedb::Connection>>>,
20}
21
22impl LanceRelationsStore {
23 pub async fn new(db_path: PathBuf) -> Result<Self> {
25 tokio::fs::create_dir_all(&db_path)
27 .await
28 .context("Failed to create relations database directory")?;
29
30 Ok(Self {
31 db_path,
32 db: Arc::new(RwLock::new(None)),
33 })
34 }
35
36 async fn get_connection(&self) -> Result<lancedb::Connection> {
38 let mut db_guard = self.db.write().await;
39
40 if let Some(ref db) = *db_guard {
41 return Ok(db.clone());
42 }
43
44 let db = lancedb::connect(self.db_path.to_string_lossy().as_ref())
45 .execute()
46 .await
47 .context("Failed to connect to LanceDB")?;
48
49 *db_guard = Some(db.clone());
50 Ok(db)
51 }
52
53 async fn ensure_definitions_table(&self) -> Result<()> {
55 let _db = self.get_connection().await?;
56 Ok(())
59 }
60
61 async fn ensure_references_table(&self) -> Result<()> {
63 let _db = self.get_connection().await?;
64 Ok(())
66 }
67}
68
69#[async_trait]
70impl RelationsStore for LanceRelationsStore {
71 async fn store_definitions(
72 &self,
73 definitions: Vec<Definition>,
74 _root_path: &str,
75 ) -> Result<usize> {
76 if definitions.is_empty() {
77 return Ok(0);
78 }
79
80 self.ensure_definitions_table().await?;
81
82 let count = definitions.len();
85
86 tracing::debug!("Stored {} definitions", count);
87 Ok(count)
88 }
89
90 async fn store_references(&self, references: Vec<Reference>, _root_path: &str) -> Result<usize> {
91 if references.is_empty() {
92 return Ok(0);
93 }
94
95 self.ensure_references_table().await?;
96
97 let count = references.len();
99
100 tracing::debug!("Stored {} references", count);
101 Ok(count)
102 }
103
104 async fn find_definition_at(
105 &self,
106 _file_path: &str,
107 _line: usize,
108 _column: usize,
109 ) -> Result<Option<Definition>> {
110 Ok(None)
112 }
113
114 async fn find_definitions_by_name(&self, _name: &str) -> Result<Vec<Definition>> {
115 Ok(Vec::new())
117 }
118
119 async fn find_references(&self, _target_symbol_id: &str) -> Result<Vec<Reference>> {
120 Ok(Vec::new())
122 }
123
124 async fn get_callers(&self, _symbol_id: &str) -> Result<Vec<CallEdge>> {
125 Ok(Vec::new())
127 }
128
129 async fn get_callees(&self, _symbol_id: &str) -> Result<Vec<CallEdge>> {
130 Ok(Vec::new())
132 }
133
134 async fn delete_by_file(&self, _file_path: &str) -> Result<usize> {
135 Ok(0)
137 }
138
139 async fn clear(&self) -> Result<()> {
140 Ok(())
142 }
143
144 async fn get_stats(&self) -> Result<RelationsStats> {
145 Ok(RelationsStats::default())
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use tempfile::TempDir;
154
155 #[tokio::test]
156 async fn test_store_creation() {
157 let temp_dir = TempDir::new().unwrap();
158 let store = LanceRelationsStore::new(temp_dir.path().to_path_buf())
159 .await
160 .unwrap();
161
162 let stats = store.get_stats().await.unwrap();
163 assert_eq!(stats.definition_count, 0);
164 }
165
166 #[tokio::test]
167 async fn test_store_empty_definitions() {
168 let temp_dir = TempDir::new().unwrap();
169 let store = LanceRelationsStore::new(temp_dir.path().to_path_buf())
170 .await
171 .unwrap();
172
173 let count = store.store_definitions(Vec::new(), "/test").await.unwrap();
174 assert_eq!(count, 0);
175 }
176}