vtcode_core/
markdown_storage.rs1use anyhow::{Context, Result};
8use indexmap::IndexMap;
9use serde::{Deserialize, Serialize};
10use std::fs;
11use std::path::PathBuf;
12
13#[derive(Clone)]
15pub struct MarkdownStorage {
16 storage_dir: PathBuf,
17}
18
19impl MarkdownStorage {
20 pub fn new(storage_dir: PathBuf) -> Self {
22 Self { storage_dir }
23 }
24
25 pub fn init(&self) -> Result<()> {
27 fs::create_dir_all(&self.storage_dir)?;
28 Ok(())
29 }
30
31 pub fn store<T: Serialize>(&self, key: &str, data: &T, title: &str) -> Result<()> {
33 let file_path = self.storage_dir.join(format!("{}.md", key));
34 let markdown = self.serialize_to_markdown(data, title)?;
35 fs::write(file_path, markdown)?;
36 Ok(())
37 }
38
39 pub fn load<T: for<'de> Deserialize<'de>>(&self, key: &str) -> Result<T> {
41 let file_path = self.storage_dir.join(format!("{}.md", key));
42 let content = fs::read_to_string(file_path)?;
43 self.deserialize_from_markdown(&content)
44 }
45
46 pub fn list(&self) -> Result<Vec<String>> {
48 let mut items = Vec::new();
49
50 for entry in fs::read_dir(&self.storage_dir)? {
51 let entry = entry?;
52 if let Some(file_name) = entry.path().file_stem() {
53 if let Some(name) = file_name.to_str() {
54 items.push(name.to_string());
55 }
56 }
57 }
58
59 Ok(items)
60 }
61
62 pub fn delete(&self, key: &str) -> Result<()> {
64 let file_path = self.storage_dir.join(format!("{}.md", key));
65 if file_path.exists() {
66 fs::remove_file(file_path)?;
67 }
68 Ok(())
69 }
70
71 pub fn exists(&self, key: &str) -> bool {
73 let file_path = self.storage_dir.join(format!("{}.md", key));
74 file_path.exists()
75 }
76
77 fn serialize_to_markdown<T: Serialize>(&self, data: &T, title: &str) -> Result<String> {
80 let json = serde_json::to_string_pretty(data)?;
81 let yaml = serde_yaml::to_string(data)?;
82
83 let markdown = format!(
84 "# {}\n\n\
85 ## JSON\n\n\
86 ```json\n\
87 {}\n\
88 ```\n\n\
89 ## YAML\n\n\
90 ```yaml\n\
91 {}\n\
92 ```\n\n\
93 ## Raw Data\n\n\
94 {}\n",
95 title,
96 json,
97 yaml,
98 self.format_raw_data(data)
99 );
100
101 Ok(markdown)
102 }
103
104 fn deserialize_from_markdown<T: for<'de> Deserialize<'de>>(&self, content: &str) -> Result<T> {
105 if let Some(json_block) = self.extract_code_block(content, "json") {
107 return serde_json::from_str(json_block).context("Failed to parse JSON from markdown");
108 }
109
110 if let Some(yaml_block) = self.extract_code_block(content, "yaml") {
112 return serde_yaml::from_str(yaml_block).context("Failed to parse YAML from markdown");
113 }
114
115 Err(anyhow::anyhow!("No valid JSON or YAML found in markdown"))
116 }
117
118 fn extract_code_block<'a>(&self, content: &'a str, language: &str) -> Option<&'a str> {
119 let start_pattern = format!("```{}", language);
120 let end_pattern = "```";
121
122 if let Some(start_idx) = content.find(&start_pattern) {
123 let code_start = start_idx + start_pattern.len();
124 if let Some(end_idx) = content[code_start..].find(end_pattern) {
125 let code_end = code_start + end_idx;
126 return Some(content[code_start..code_end].trim());
127 }
128 }
129
130 None
131 }
132
133 fn format_raw_data<T: Serialize>(&self, data: &T) -> String {
134 match serde_json::to_value(data) {
135 Ok(serde_json::Value::Object(map)) => {
136 let mut lines = Vec::new();
137 for (key, value) in map {
138 lines.push(format!("- **{}**: {}", key, self.format_value(&value)));
139 }
140 lines.join("\n")
141 }
142 _ => "Complex data structure".to_string(),
143 }
144 }
145
146 fn format_value(&self, value: &serde_json::Value) -> String {
147 match value {
148 serde_json::Value::String(s) => format!("\"{}\"", s),
149 serde_json::Value::Number(n) => n.to_string(),
150 serde_json::Value::Bool(b) => b.to_string(),
151 serde_json::Value::Array(arr) => format!("[{} items]", arr.len()),
152 serde_json::Value::Object(obj) => format!("{{{} fields}}", obj.len()),
153 serde_json::Value::Null => "null".to_string(),
154 }
155 }
156}
157
158pub struct SimpleKVStorage {
160 storage: MarkdownStorage,
161}
162
163impl SimpleKVStorage {
164 pub fn new(storage_dir: PathBuf) -> Self {
165 Self {
166 storage: MarkdownStorage::new(storage_dir),
167 }
168 }
169
170 pub fn init(&self) -> Result<()> {
171 self.storage.init()
172 }
173
174 pub fn put(&self, key: &str, value: &str) -> Result<()> {
175 let data = IndexMap::from([("value".to_string(), value.to_string())]);
176 self.storage
177 .store(key, &data, &format!("Key-Value: {}", key))
178 }
179
180 pub fn get(&self, key: &str) -> Result<String> {
181 let data: IndexMap<String, String> = self.storage.load(key)?;
182 data.get("value")
183 .cloned()
184 .ok_or_else(|| anyhow::anyhow!("Value not found for key: {}", key))
185 }
186
187 pub fn delete(&self, key: &str) -> Result<()> {
188 self.storage.delete(key)
189 }
190
191 pub fn list_keys(&self) -> Result<Vec<String>> {
192 self.storage.list()
193 }
194}
195
196#[derive(Debug, Clone, Serialize, Deserialize)]
198pub struct ProjectData {
199 pub name: String,
200 pub description: Option<String>,
201 pub version: String,
202 pub tags: Vec<String>,
203 pub metadata: IndexMap<String, String>,
204}
205
206impl ProjectData {
207 pub fn new(name: &str) -> Self {
208 Self {
209 name: name.to_string(),
210 description: None,
211 version: "1.0.0".to_string(),
212 tags: vec![],
213 metadata: IndexMap::new(),
214 }
215 }
216}
217
218#[derive(Clone)]
220pub struct ProjectStorage {
221 storage: MarkdownStorage,
222}
223
224impl ProjectStorage {
225 pub fn new(storage_dir: PathBuf) -> Self {
226 Self {
227 storage: MarkdownStorage::new(storage_dir),
228 }
229 }
230
231 pub fn init(&self) -> Result<()> {
232 self.storage.init()
233 }
234
235 pub fn save_project(&self, project: &ProjectData) -> Result<()> {
236 self.storage.store(
237 &project.name,
238 project,
239 &format!("Project: {}", project.name),
240 )
241 }
242
243 pub fn load_project(&self, name: &str) -> Result<ProjectData> {
244 self.storage.load(name)
245 }
246
247 pub fn list_projects(&self) -> Result<Vec<String>> {
248 self.storage.list()
249 }
250
251 pub fn delete_project(&self, name: &str) -> Result<()> {
252 self.storage.delete(name)
253 }
254}