oxios_memory/memory/
root_index.rs1#![allow(missing_docs)]
2use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9
10use crate::memory::types::{MemoryType, ProtectionLevel};
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct RootIndex {
22 pub version: u64,
24 pub updated_at: DateTime<Utc>,
26 pub active_context: Vec<RootEntry>,
28 pub recent_patterns: Vec<String>,
30 pub historical_summary: Vec<HistoricalPeriod>,
32 pub topics: Vec<TopicEntry>,
34}
35
36impl Default for RootIndex {
37 fn default() -> Self {
38 Self {
39 version: 0,
40 updated_at: Utc::now(),
41 active_context: Vec::new(),
42 recent_patterns: Vec::new(),
43 historical_summary: Vec::new(),
44 topics: Vec::new(),
45 }
46 }
47}
48
49impl RootIndex {
50 pub fn new() -> Self {
52 Self::default()
53 }
54
55 pub fn estimated_tokens(&self) -> usize {
57 let total_chars: usize = self
58 .active_context
59 .iter()
60 .map(|e| e.topic.len() + e.reference.len())
61 .chain(self.recent_patterns.iter().map(|p| p.len()))
62 .chain(
63 self.topics
64 .iter()
65 .map(|t| t.name.len() + t.description.len()),
66 )
67 .sum();
68 total_chars / 4
69 }
70
71 pub fn topic_matches_query(&self, topic: &TopicEntry, query: &str) -> bool {
73 let query_lower = query.to_lowercase();
74 topic.name.to_lowercase().contains(&query_lower)
75 || topic.description.to_lowercase().contains(&query_lower)
76 || topic.category.to_lowercase().contains(&query_lower)
77 }
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct RootEntry {
87 pub topic: String,
89 pub memory_type: MemoryType,
91 pub protection: ProtectionLevel,
93 pub age_days: u32,
95 pub reference: String,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct HistoricalPeriod {
102 pub period: String,
104 pub summary: String,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct TopicEntry {
111 pub name: String,
113 pub category: String,
115 pub age_days: u32,
117 pub description: String,
119 pub reference: String,
121}
122
123#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn test_root_index_default() {
133 let idx = RootIndex::default();
134 assert_eq!(idx.version, 0);
135 assert!(idx.active_context.is_empty());
136 assert!(idx.topics.is_empty());
137 }
138
139 #[test]
140 fn test_estimated_tokens() {
141 let mut idx = RootIndex::new();
142 idx.topics.push(TopicEntry {
143 name: "Rust async runtime".to_string(),
144 category: "project".to_string(),
145 age_days: 5,
146 description: "Using Tokio for async".to_string(),
147 reference: "fact-123".to_string(),
148 });
149 let tokens = idx.estimated_tokens();
150 assert!(tokens > 0, "Should have some estimated tokens");
151 }
152
153 #[test]
154 fn test_topic_matches_query() {
155 let idx = RootIndex::new();
156 let topic = TopicEntry {
157 name: "Memory consolidation".to_string(),
158 category: "architecture".to_string(),
159 age_days: 3,
160 description: "RFC-008 tiered memory system".to_string(),
161 reference: "dec-456".to_string(),
162 };
163 assert!(idx.topic_matches_query(&topic, "memory"));
164 assert!(idx.topic_matches_query(&topic, "consolidation"));
165 assert!(idx.topic_matches_query(&topic, "architecture"));
166 assert!(!idx.topic_matches_query(&topic, "deployment"));
167 }
168}