1use std::collections::HashMap;
17use std::ops::Range;
18use std::sync::Arc;
19use g_math::fixed_point::FixedPoint;
20use log::trace;
21use super::tree_tensor::{HyperbolicTreeTensor, HTTConfig, SharedHTT, IntegrationError, IntegrationResult};
22use super::config::HTTStorageConfig;
23
24pub struct HTTStorage {
29 htt: SharedHTT,
31 config: HTTStorageConfig,
33}
34
35impl HTTStorage {
36 pub fn new(config: HTTStorageConfig) -> Self {
38 let mut htt_config = HTTConfig::new(
39 config.dimension,
40 config.max_memory_nodes,
41 config.cache_size,
42 );
43 if config.tau > FixedPoint::from_int(0) {
44 htt_config = htt_config.with_tau(config.tau);
45 }
46
47 let htt = Arc::new(HyperbolicTreeTensor::new(htt_config));
48
49 htt.insert("/", vec![], Some("application/x-directory".to_string()))
55 .expect("failed to initialize HTT root node ('/')");
56
57 Self { htt, config }
58 }
59
60 pub fn shared_htt(&self) -> &SharedHTT {
62 &self.htt
63 }
64
65 pub fn store_data_only(&self, key: &str, value: &[u8], content_type: Option<String>) -> IntegrationResult<()> {
70 let normalized_key = Self::normalize_key(key);
71
72 let missing_ancestors = Self::find_missing_ancestors(&self.htt, &normalized_key);
73 for ancestor in &missing_ancestors {
74 if !self.htt.exists(ancestor) {
75 match self.htt.insert_data_only(ancestor, vec![], Some("application/x-directory".to_string())) {
76 Ok(()) => {},
77 Err(IntegrationError::AlreadyExists(_)) => {},
78 Err(e) => return Err(e),
79 }
80 }
81 }
82
83 if self.htt.exists(&normalized_key) {
84 self.htt.update_value(&normalized_key, value.to_vec())?;
85 } else {
86 match self.htt.insert_data_only(&normalized_key, value.to_vec(), content_type) {
87 Ok(()) => {},
88 Err(IntegrationError::AlreadyExists(_)) => {
89 self.htt.update_value(&normalized_key, value.to_vec())?;
90 },
91 Err(e) => return Err(e),
92 }
93 }
94
95 Ok(())
96 }
97
98 pub fn store(&self, key: &str, value: &[u8], content_type: Option<String>) -> IntegrationResult<()> {
104 trace!("HTTStorage::store - key: {}, size: {} bytes", key, value.len());
105
106 let normalized_key = Self::normalize_key(key);
107
108 let missing_ancestors = Self::find_missing_ancestors(&self.htt, &normalized_key);
110
111 for ancestor in &missing_ancestors {
115 if !self.htt.exists(ancestor) {
116 match self.htt.insert(
117 ancestor,
118 vec![],
119 Some("application/x-directory".to_string()),
120 ) {
121 Ok(()) => {},
122 Err(IntegrationError::AlreadyExists(_)) => {},
123 Err(e) => return Err(e),
124 }
125 }
126 }
127
128 if self.htt.exists(&normalized_key) {
132 self.htt.update_value(&normalized_key, value.to_vec())?;
133 } else {
134 match self.htt.insert(&normalized_key, value.to_vec(), content_type) {
135 Ok(()) => {},
136 Err(IntegrationError::AlreadyExists(_)) => {
137 self.htt.update_value(&normalized_key, value.to_vec())?;
138 },
139 Err(e) => return Err(e),
140 }
141 }
142
143 Ok(())
144 }
145
146 pub fn store_positioned(&self, key: &str, value: &[u8], content_type: Option<String>, child_index: u32) -> IntegrationResult<()> {
151 let normalized_key = Self::normalize_key(key);
152
153 let missing_ancestors = Self::find_missing_ancestors(&self.htt, &normalized_key);
154 for ancestor in &missing_ancestors {
155 if !self.htt.exists(ancestor) {
156 match self.htt.insert(
157 ancestor,
158 vec![],
159 Some("application/x-directory".to_string()),
160 ) {
161 Ok(()) => {},
162 Err(IntegrationError::AlreadyExists(_)) => {},
163 Err(e) => return Err(e),
164 }
165 }
166 }
167
168 if self.htt.exists(&normalized_key) {
169 self.htt.update_value(&normalized_key, value.to_vec())?;
170 } else {
171 match self.htt.insert_positioned(&normalized_key, value.to_vec(), content_type, child_index) {
172 Ok(()) => {},
173 Err(IntegrationError::AlreadyExists(_)) => {
174 self.htt.update_value(&normalized_key, value.to_vec())?;
175 },
176 Err(e) => return Err(e),
177 }
178 }
179
180 Ok(())
181 }
182
183 pub fn retrieve(&self, key: &str) -> IntegrationResult<Vec<u8>> {
185 trace!("HTTStorage::retrieve - key: {}", key);
186
187 let normalized_key = Self::normalize_key(key);
188 let node = self.htt.get(&normalized_key)?;
189 Ok(node.value().to_vec())
190 }
191
192 pub fn delete(&self, key: &str) -> IntegrationResult<()> {
194 trace!("HTTStorage::delete - key: {}", key);
195
196 let normalized_key = Self::normalize_key(key);
197
198 if normalized_key == "/" {
199 return Err(IntegrationError::ValidationFailed(
200 "Cannot delete root node".to_string(),
201 ));
202 }
203
204 self.htt.delete(&normalized_key)
205 }
206
207 pub fn list(&self, prefix: &str) -> IntegrationResult<Vec<String>> {
209 trace!("HTTStorage::list - prefix: {}", prefix);
210
211 let normalized_prefix = Self::normalize_key(prefix);
212 self.htt.list_subtree(&normalized_prefix)
213 }
214
215 pub fn exists(&self, key: &str) -> bool {
217 let normalized_key = Self::normalize_key(key);
218 self.htt.exists(&normalized_key)
219 }
220
221 pub fn get_metadata(&self, key: &str) -> IntegrationResult<HashMap<String, String>> {
223 trace!("HTTStorage::get_metadata - key: {}", key);
224
225 let normalized_key = Self::normalize_key(key);
226 let node = self.htt.get(&normalized_key)?;
227 let meta = node.metadata();
228
229 let mut result = meta.metadata.clone();
230 result.insert("key".to_string(), meta.key.clone());
231 result.insert("size".to_string(), node.value().len().to_string());
232 result.insert("created_at".to_string(), meta.created_at.to_string());
233 result.insert("updated_at".to_string(), meta.updated_at.to_string());
234
235 if let Some(ref ct) = meta.content_type {
236 result.insert("content_type".to_string(), ct.clone());
237 }
238
239 Ok(result)
240 }
241
242 pub fn set_metadata(&self, key: &str, meta_key: &str, meta_value: &str) -> IntegrationResult<()> {
244 trace!("HTTStorage::set_metadata - key: {}, meta_key: {}", key, meta_key);
245
246 let normalized_key = Self::normalize_key(key);
247 self.htt.set_node_metadata(&normalized_key, meta_key, meta_value)
248 }
249
250 pub fn set_semantic(&self, key: &str, coords: Vec<u8>) -> IntegrationResult<()> {
252 trace!("HTTStorage::set_semantic - key: {}, bytes: {}", key, coords.len());
253
254 let normalized_key = Self::normalize_key(key);
255 self.htt.set_semantic(&normalized_key, coords)
256 }
257
258 pub fn get_semantic(&self, key: &str) -> IntegrationResult<Vec<u8>> {
260 trace!("HTTStorage::get_semantic - key: {}", key);
261
262 let normalized_key = Self::normalize_key(key);
263 self.htt.get_semantic(&normalized_key)
264 }
265
266 pub fn position(&self, key: &str) -> IntegrationResult<crate::hyperbolic_geometry::HyperbolicPoint> {
268 let normalized_key = Self::normalize_key(key);
269 self.htt.position(&normalized_key)
270 }
271
272 pub fn embed_existing(&self, key: &str) -> IntegrationResult<bool> {
276 let normalized_key = Self::normalize_key(key);
277 self.htt.embed_existing(&normalized_key)
278 }
279
280 pub fn semantic_epoch(&self) -> u64 {
283 self.htt.tensor_network().semantic_epoch()
284 }
285
286 pub fn find_nearest(&self, path: &str, k: usize) -> IntegrationResult<Vec<String>> {
289 let normalized = Self::normalize_key(path);
290 let results = self.htt.find_nearest(&normalized, k)?;
291 Ok(results.into_iter().map(|(p, _dist)| p).collect())
292 }
293
294 pub fn find_in_radius(&self, path: &str, radius: FixedPoint) -> IntegrationResult<Vec<String>> {
296 let normalized = Self::normalize_key(path);
297 let results = self.htt.find_in_radius(&normalized, radius)?;
298 Ok(results.into_iter().map(|(p, _dist)| p).collect())
299 }
300
301 pub fn nearest_semantic(
313 &self,
314 query_coords: &[u8],
315 k: usize,
316 dim_range: &Range<usize>,
317 ) -> IntegrationResult<Vec<(String, FixedPoint)>> {
318 self.htt.nearest_semantic(query_coords, k, dim_range)
319 }
320
321 pub fn neighbors_semantic(
324 &self,
325 path: &str,
326 k: usize,
327 dim_range: &Range<usize>,
328 ) -> IntegrationResult<Vec<(String, FixedPoint)>> {
329 let normalized = Self::normalize_key(path);
330 self.htt.neighbors_semantic(&normalized, k, dim_range)
331 }
332
333 pub fn nearest_neighbor_point(&self, coords: &[FixedPoint]) -> IntegrationResult<(String, FixedPoint)> {
346 self.validate_query_coords(coords)?;
347 let query = super::hyperbolic_geometry::HyperbolicPoint::from_slice(coords);
348 self.htt.nearest_neighbor_point(&query)
349 }
350
351 pub fn nearest_neighbor_point_k(&self, coords: &[FixedPoint], k: usize) -> IntegrationResult<Vec<(String, FixedPoint)>> {
355 self.validate_query_coords(coords)?;
356 let query = super::hyperbolic_geometry::HyperbolicPoint::from_slice(coords);
357 self.htt.nearest_neighbor_point_k(&query, k)
358 }
359
360 fn validate_query_coords(&self, coords: &[FixedPoint]) -> IntegrationResult<()> {
364 if coords.len() != self.config.dimension {
365 return Err(IntegrationError::ValidationFailed(format!(
366 "query has {} coordinates but the store dimension is {}",
367 coords.len(),
368 self.config.dimension
369 )));
370 }
371 Ok(())
372 }
373
374 fn find_missing_ancestors(htt: &HyperbolicTreeTensor, path: &str) -> Vec<String> {
377 let mut missing = Vec::new();
378 let mut current = path.to_string();
379
380 loop {
381 let parent = match current.rfind('/') {
382 Some(index) if index > 0 => current[0..index].to_string(),
383 Some(0) if current != "/" => "/".to_string(),
384 _ => break,
385 };
386
387 if parent == current {
388 break;
389 }
390
391 if htt.exists(&parent) {
392 break;
393 }
394
395 missing.push(parent.clone());
396 current = parent;
397 }
398
399 missing.reverse(); missing
401 }
402
403 pub fn node_count(&self) -> usize {
405 self.htt.node_count()
406 }
407
408 pub fn stats(&self) -> HashMap<String, String> {
410 let mut stats = HashMap::new();
411
412 for (key, value) in self.htt.stats() {
413 stats.insert(format!("htt.{}", key), value);
414 }
415 stats.insert("node_count".to_string(), self.htt.node_count().to_string());
416
417 stats.insert("dimension".to_string(), self.config.dimension.to_string());
418 stats.insert(
419 "max_memory_nodes".to_string(),
420 self.config.max_memory_nodes.to_string(),
421 );
422 stats.insert("cache_size".to_string(), self.config.cache_size.to_string());
423
424 stats
425 }
426
427 fn normalize_key(key: &str) -> String {
429 if !key.starts_with('/') {
430 format!("/{}", key)
431 } else {
432 key.to_string()
433 }
434 }
435}
436
437#[cfg(test)]
438mod tests {
439
440fn fp(vals: &[f64]) -> Vec<g_math::fixed_point::FixedPoint> {
442 vals.iter().map(|&v| g_math::fixed_point::FixedPoint::from_f64(v)).collect()
443}
444
445 use super::*;
446
447 #[test]
448 fn test_storage_creation() {
449 let config = HTTStorageConfig::default();
450 let storage = HTTStorage::new(config);
451 assert!(storage.exists("/"));
452 }
453
454 #[test]
455 fn test_storage_operations() {
456 let config = HTTStorageConfig::default();
457 let storage = HTTStorage::new(config);
458
459 storage.store("/test", b"test data", None).unwrap();
461 assert!(storage.exists("/test"));
462
463 let data = storage.retrieve("/test").unwrap();
465 assert_eq!(data, b"test data");
466
467 storage.store("/test", b"updated data", None).unwrap();
469 let updated = storage.retrieve("/test").unwrap();
470 assert_eq!(updated, b"updated data");
471
472 storage.store("/parent/child", b"child data", None).unwrap();
474 assert!(storage.exists("/parent"));
475
476 let keys = storage.list("/").unwrap();
478 assert!(keys.contains(&"/test".to_string()));
479 assert!(keys.contains(&"/parent".to_string()));
480 assert!(keys.contains(&"/parent/child".to_string()));
481
482 storage.delete("/test").unwrap();
484 assert!(!storage.exists("/test"));
485
486 storage
488 .set_metadata("/parent", "description", "A parent directory")
489 .unwrap();
490 let metadata = storage.get_metadata("/parent").unwrap();
491 assert_eq!(
492 metadata.get("description"),
493 Some(&"A parent directory".to_string())
494 );
495 }
496
497 #[test]
498 fn test_find_nearest_api() {
499 let config = HTTStorageConfig::default();
500 let storage = HTTStorage::new(config);
501
502 storage.store("/a", b"a", None).unwrap();
503 storage.store("/b", b"b", None).unwrap();
504 storage.store("/c", b"c", None).unwrap();
505
506 let nearest = storage.find_nearest("/a", 2).unwrap();
507 assert!(!nearest.is_empty());
508 assert!(nearest.len() <= 2);
509 assert!(!nearest.contains(&"/a".to_string()));
511 }
512
513 #[test]
514 fn test_find_in_radius_api() {
515 use g_math::fixed_point::FixedPoint;
516
517 let config = HTTStorageConfig::default();
518 let storage = HTTStorage::new(config);
519
520 storage.store("/x", b"x", None).unwrap();
521 storage.store("/y", b"y", None).unwrap();
522
523 let large_radius = FixedPoint::from_int(10);
524 let results = storage.find_in_radius("/x", large_radius).unwrap();
525 assert!(!results.is_empty());
527 }
528
529 #[test]
530 fn test_storage_stats() {
531 let config = HTTStorageConfig::default();
532 let storage = HTTStorage::new(config);
533
534 storage.store("/test1", b"data1", None).unwrap();
535 storage.store("/test2", b"data2", None).unwrap();
536
537 let stats = storage.stats();
538 assert!(stats.contains_key("node_count"));
539 assert!(stats.contains_key("dimension"));
540 }
541
542 #[test]
543 fn test_nearest_neighbor_point_api() {
544 let config = HTTStorageConfig::default();
545 let storage = HTTStorage::new(config);
546
547 storage.store("/a", b"a", None).unwrap();
548 storage.store("/b", b"b", None).unwrap();
549 storage.store("/c", b"c", None).unwrap();
550
551 let (path, dist) = storage.nearest_neighbor_point(&fp(&[0.0, 0.0, 0.0, 0.0])).unwrap();
553 assert_eq!(path, "/", "Query at origin should find root");
554 let tolerance = FixedPoint::from_int(1) / FixedPoint::from_int(10);
555 assert!(dist < tolerance, "Distance to root at origin should be small");
556 }
557}