agentic_evolve_core/storage/
store.rs1use std::collections::HashMap;
4use std::path::{Path, PathBuf};
5
6use crate::types::error::{EvolveError, EvolveResult};
7use crate::types::pattern::Pattern;
8
9#[derive(Debug)]
11pub struct PatternStore {
12 patterns: HashMap<String, Pattern>,
13 data_dir: Option<PathBuf>,
14}
15
16impl PatternStore {
17 pub fn new() -> Self {
18 Self {
19 patterns: HashMap::new(),
20 data_dir: None,
21 }
22 }
23
24 pub fn with_data_dir(data_dir: &Path) -> EvolveResult<Self> {
25 std::fs::create_dir_all(data_dir)?;
26 let mut store = Self {
27 patterns: HashMap::new(),
28 data_dir: Some(data_dir.to_path_buf()),
29 };
30 store.load_all()?;
31 Ok(store)
32 }
33
34 pub fn save(&mut self, pattern: &Pattern) -> EvolveResult<()> {
35 self.patterns
36 .insert(pattern.id.as_str().to_string(), pattern.clone());
37 if let Some(dir) = &self.data_dir {
38 let path = dir.join(format!("{}.json", pattern.id.as_str()));
39 let json = serde_json::to_string_pretty(pattern)?;
40 std::fs::write(path, json)?;
41 }
42 Ok(())
43 }
44
45 pub fn get(&self, id: &str) -> EvolveResult<&Pattern> {
46 self.patterns
47 .get(id)
48 .ok_or_else(|| EvolveError::PatternNotFound(id.to_string()))
49 }
50
51 pub fn get_mut(&mut self, id: &str) -> EvolveResult<&mut Pattern> {
52 self.patterns
53 .get_mut(id)
54 .ok_or_else(|| EvolveError::PatternNotFound(id.to_string()))
55 }
56
57 pub fn delete(&mut self, id: &str) -> EvolveResult<Pattern> {
58 let pattern = self
59 .patterns
60 .remove(id)
61 .ok_or_else(|| EvolveError::PatternNotFound(id.to_string()))?;
62 if let Some(dir) = &self.data_dir {
63 let path = dir.join(format!("{id}.json"));
64 if path.exists() {
65 std::fs::remove_file(path)?;
66 }
67 }
68 Ok(pattern)
69 }
70
71 pub fn list(&self) -> Vec<&Pattern> {
72 self.patterns.values().collect()
73 }
74
75 pub fn list_by_domain(&self, domain: &str) -> Vec<&Pattern> {
76 self.patterns
77 .values()
78 .filter(|p| p.domain == domain)
79 .collect()
80 }
81
82 pub fn list_by_language(&self, language: &str) -> Vec<&Pattern> {
83 self.patterns
84 .values()
85 .filter(|p| p.language.as_str() == language)
86 .collect()
87 }
88
89 pub fn search(&self, query: &str) -> Vec<&Pattern> {
90 let query_lower = query.to_lowercase();
91 self.patterns
92 .values()
93 .filter(|p| {
94 p.name.to_lowercase().contains(&query_lower)
95 || p.domain.to_lowercase().contains(&query_lower)
96 || p.template.to_lowercase().contains(&query_lower)
97 || p.tags
98 .iter()
99 .any(|t| t.to_lowercase().contains(&query_lower))
100 })
101 .collect()
102 }
103
104 pub fn count(&self) -> usize {
105 self.patterns.len()
106 }
107
108 pub fn contains(&self, id: &str) -> bool {
109 self.patterns.contains_key(id)
110 }
111
112 pub fn clear(&mut self) -> EvolveResult<()> {
113 self.patterns.clear();
114 if let Some(dir) = &self.data_dir {
115 if dir.exists() {
116 for entry in std::fs::read_dir(dir)? {
117 let entry = entry?;
118 if entry.path().extension().is_some_and(|ext| ext == "json") {
119 std::fs::remove_file(entry.path())?;
120 }
121 }
122 }
123 }
124 Ok(())
125 }
126
127 fn load_all(&mut self) -> EvolveResult<()> {
128 let dir = match &self.data_dir {
129 Some(d) => d.clone(),
130 None => return Ok(()),
131 };
132 if !dir.exists() {
133 return Ok(());
134 }
135 for entry in std::fs::read_dir(&dir)? {
136 let entry = entry?;
137 let path = entry.path();
138 if path.extension().is_some_and(|ext| ext == "json") {
139 let content = std::fs::read_to_string(&path)?;
140 match serde_json::from_str::<Pattern>(&content) {
141 Ok(pattern) => {
142 self.patterns
143 .insert(pattern.id.as_str().to_string(), pattern);
144 }
145 Err(e) => {
146 tracing::warn!("Failed to load pattern from {:?}: {}", path, e);
147 }
148 }
149 }
150 }
151 Ok(())
152 }
153}
154
155impl Default for PatternStore {
156 fn default() -> Self {
157 Self::new()
158 }
159}