ricecoder_storage/
global_store.rs1use crate::error::{IoOperation, StorageError, StorageResult};
6use crate::manager::PathResolver;
7use crate::types::ResourceType;
8use std::fs;
9use std::path::{Path, PathBuf};
10
11pub struct GlobalStore {
13 base_path: PathBuf,
15}
16
17impl GlobalStore {
18 pub fn new(base_path: PathBuf) -> Self {
20 GlobalStore { base_path }
21 }
22
23 pub fn with_default_path() -> StorageResult<Self> {
25 let base_path = PathResolver::resolve_global_path()?;
26 Ok(GlobalStore { base_path })
27 }
28
29 pub fn base_path(&self) -> &PathBuf {
31 &self.base_path
32 }
33
34 pub fn initialize(&self) -> StorageResult<()> {
45 self.create_dir_if_not_exists(&self.base_path)?;
47
48 for resource_type in &[
50 ResourceType::Template,
51 ResourceType::Standard,
52 ResourceType::Spec,
53 ResourceType::Steering,
54 ResourceType::Boilerplate,
55 ResourceType::Rule,
56 ] {
57 let resource_path = self.resource_path(*resource_type);
58 self.create_dir_if_not_exists(&resource_path)?;
59 }
60
61 let cache_path = self.base_path.join("cache");
63 self.create_dir_if_not_exists(&cache_path)?;
64
65 Ok(())
66 }
67
68 pub fn resource_path(&self, resource_type: ResourceType) -> PathBuf {
70 self.base_path.join(resource_type.dir_name())
71 }
72
73 pub fn store_resource(
75 &self,
76 resource_type: ResourceType,
77 name: &str,
78 content: &[u8],
79 ) -> StorageResult<PathBuf> {
80 let resource_dir = self.resource_path(resource_type);
81 let file_path = resource_dir.join(name);
82
83 self.create_dir_if_not_exists(&resource_dir)?;
85
86 fs::write(&file_path, content)
88 .map_err(|e| StorageError::io_error(file_path.clone(), IoOperation::Write, e))?;
89
90 Ok(file_path)
91 }
92
93 pub fn retrieve_resource(
95 &self,
96 resource_type: ResourceType,
97 name: &str,
98 ) -> StorageResult<Vec<u8>> {
99 let resource_dir = self.resource_path(resource_type);
100 let file_path = resource_dir.join(name);
101
102 fs::read(&file_path).map_err(|e| StorageError::io_error(file_path, IoOperation::Read, e))
103 }
104
105 pub fn list_resources(&self, resource_type: ResourceType) -> StorageResult<Vec<String>> {
107 let resource_dir = self.resource_path(resource_type);
108
109 if !resource_dir.exists() {
110 return Ok(Vec::new());
111 }
112
113 let mut resources = Vec::new();
114 let entries = fs::read_dir(&resource_dir)
115 .map_err(|e| StorageError::io_error(resource_dir.clone(), IoOperation::Read, e))?;
116
117 for entry in entries {
118 let entry = entry
119 .map_err(|e| StorageError::io_error(resource_dir.clone(), IoOperation::Read, e))?;
120
121 let path = entry.path();
122 if path.is_file() {
123 if let Some(file_name) = path.file_name() {
124 if let Some(name_str) = file_name.to_str() {
125 resources.push(name_str.to_string());
126 }
127 }
128 }
129 }
130
131 Ok(resources)
132 }
133
134 pub fn delete_resource(&self, resource_type: ResourceType, name: &str) -> StorageResult<()> {
136 let resource_dir = self.resource_path(resource_type);
137 let file_path = resource_dir.join(name);
138
139 if file_path.exists() {
140 fs::remove_file(&file_path)
141 .map_err(|e| StorageError::io_error(file_path, IoOperation::Delete, e))?;
142 }
143
144 Ok(())
145 }
146
147 pub fn resource_exists(&self, resource_type: ResourceType, name: &str) -> bool {
149 let resource_dir = self.resource_path(resource_type);
150 let file_path = resource_dir.join(name);
151 file_path.exists()
152 }
153
154 fn create_dir_if_not_exists(&self, path: &Path) -> StorageResult<()> {
156 if !path.exists() {
157 fs::create_dir_all(path)
158 .map_err(|e| StorageError::directory_creation_failed(path.to_path_buf(), e))?;
159 }
160 Ok(())
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use tempfile::TempDir;
168
169 #[test]
170 fn test_global_store_initialization() {
171 let temp_dir = TempDir::new().expect("Failed to create temp dir");
172 let store = GlobalStore::new(temp_dir.path().to_path_buf());
173
174 store.initialize().expect("Failed to initialize store");
175
176 assert!(store.resource_path(ResourceType::Template).exists());
178 assert!(store.resource_path(ResourceType::Standard).exists());
179 assert!(store.resource_path(ResourceType::Spec).exists());
180 assert!(store.resource_path(ResourceType::Steering).exists());
181 assert!(store.resource_path(ResourceType::Boilerplate).exists());
182 assert!(store.resource_path(ResourceType::Rule).exists());
183 assert!(temp_dir.path().join("cache").exists());
184 }
185
186 #[test]
187 fn test_store_and_retrieve_resource() {
188 let temp_dir = TempDir::new().expect("Failed to create temp dir");
189 let store = GlobalStore::new(temp_dir.path().to_path_buf());
190 store.initialize().expect("Failed to initialize store");
191
192 let content = b"test content";
193 let name = "test.txt";
194
195 let path = store
197 .store_resource(ResourceType::Template, name, content)
198 .expect("Failed to store resource");
199
200 assert!(path.exists());
201
202 let retrieved = store
204 .retrieve_resource(ResourceType::Template, name)
205 .expect("Failed to retrieve resource");
206
207 assert_eq!(retrieved, content);
208 }
209
210 #[test]
211 fn test_list_resources() {
212 let temp_dir = TempDir::new().expect("Failed to create temp dir");
213 let store = GlobalStore::new(temp_dir.path().to_path_buf());
214 store.initialize().expect("Failed to initialize store");
215
216 store
218 .store_resource(ResourceType::Template, "template1.txt", b"content1")
219 .expect("Failed to store");
220 store
221 .store_resource(ResourceType::Template, "template2.txt", b"content2")
222 .expect("Failed to store");
223
224 let resources = store
226 .list_resources(ResourceType::Template)
227 .expect("Failed to list resources");
228
229 assert_eq!(resources.len(), 2);
230 assert!(resources.contains(&"template1.txt".to_string()));
231 assert!(resources.contains(&"template2.txt".to_string()));
232 }
233
234 #[test]
235 fn test_delete_resource() {
236 let temp_dir = TempDir::new().expect("Failed to create temp dir");
237 let store = GlobalStore::new(temp_dir.path().to_path_buf());
238 store.initialize().expect("Failed to initialize store");
239
240 let name = "test.txt";
241 store
242 .store_resource(ResourceType::Template, name, b"content")
243 .expect("Failed to store");
244
245 assert!(store.resource_exists(ResourceType::Template, name));
246
247 store
248 .delete_resource(ResourceType::Template, name)
249 .expect("Failed to delete");
250
251 assert!(!store.resource_exists(ResourceType::Template, name));
252 }
253
254 #[test]
255 fn test_resource_exists() {
256 let temp_dir = TempDir::new().expect("Failed to create temp dir");
257 let store = GlobalStore::new(temp_dir.path().to_path_buf());
258 store.initialize().expect("Failed to initialize store");
259
260 let name = "test.txt";
261 assert!(!store.resource_exists(ResourceType::Template, name));
262
263 store
264 .store_resource(ResourceType::Template, name, b"content")
265 .expect("Failed to store");
266
267 assert!(store.resource_exists(ResourceType::Template, name));
268 }
269}