1use crate::{error::Result, role::Role};
4use dashmap::DashMap;
5use std::sync::Arc;
6
7pub trait Storage: Send + Sync {
9 fn store_role(&mut self, role: Role) -> Result<()>;
11
12 fn get_role(&self, name: &str) -> Result<Option<Role>>;
14
15 fn role_exists(&self, name: &str) -> Result<bool>;
17
18 fn delete_role(&mut self, name: &str) -> Result<bool>;
20
21 fn list_roles(&self) -> Result<Vec<String>>;
23
24 fn update_role(&mut self, role: Role) -> Result<()>;
26}
27
28#[derive(Debug, Default, Clone)]
30pub struct MemoryStorage {
31 roles: Arc<DashMap<String, Role>>,
32}
33
34impl MemoryStorage {
35 pub fn new() -> Self {
37 Self {
38 roles: Arc::new(DashMap::new()),
39 }
40 }
41
42 pub fn role_count(&self) -> usize {
44 self.roles.len()
45 }
46
47 pub fn clear(&mut self) {
49 self.roles.clear();
50 }
51}
52
53impl Storage for MemoryStorage {
54 fn store_role(&mut self, role: Role) -> Result<()> {
55 let name = role.name().to_string();
56 self.roles.insert(name, role);
57 Ok(())
58 }
59
60 fn get_role(&self, name: &str) -> Result<Option<Role>> {
61 Ok(self.roles.get(name).map(|r| r.clone()))
62 }
63
64 fn role_exists(&self, name: &str) -> Result<bool> {
65 Ok(self.roles.contains_key(name))
66 }
67
68 fn delete_role(&mut self, name: &str) -> Result<bool> {
69 Ok(self.roles.remove(name).is_some())
70 }
71
72 fn list_roles(&self) -> Result<Vec<String>> {
73 Ok(self.roles.iter().map(|entry| entry.key().clone()).collect())
74 }
75
76 fn update_role(&mut self, role: Role) -> Result<()> {
77 let name = role.name().to_string();
78 self.roles.insert(name, role);
79 Ok(())
80 }
81}
82
83#[cfg(feature = "persistence")]
85pub mod file_storage {
86 use super::*;
87 use crate::error::Error;
88 use std::{
89 collections::HashMap,
90 fs::{File, OpenOptions},
91 io::{BufReader, BufWriter},
92 path::{Path, PathBuf},
93 sync::RwLock,
94 };
95
96 #[derive(Debug)]
98 pub struct FileStorage {
99 storage_path: PathBuf,
100 roles: Arc<RwLock<HashMap<String, Role>>>,
101 }
102
103 impl FileStorage {
104 pub fn new(storage_path: impl AsRef<Path>) -> Result<Self> {
106 let storage_path = storage_path.as_ref().to_path_buf();
107
108 if let Some(parent) = storage_path.parent() {
110 std::fs::create_dir_all(parent).map_err(|e| {
111 Error::Storage(format!("Failed to create storage directory: {}", e))
112 })?;
113 }
114
115 let mut storage = Self {
116 storage_path,
117 roles: Arc::new(RwLock::new(HashMap::new())),
118 };
119
120 storage.load_from_disk()?;
122
123 Ok(storage)
124 }
125
126 fn load_from_disk(&mut self) -> Result<()> {
128 if !self.storage_path.exists() {
129 return Ok(());
130 }
131
132 let file = File::open(&self.storage_path).map_err(|e| {
133 Error::Storage(format!("Failed to open storage file: {}", e))
134 })?;
135
136 let reader = BufReader::new(file);
137 let roles: HashMap<String, Role> = serde_json::from_reader(reader)?;
138
139 *self.roles.write().unwrap() = roles;
140 Ok(())
141 }
142
143 fn save_to_disk(&self) -> Result<()> {
145 let file = OpenOptions::new()
146 .write(true)
147 .create(true)
148 .truncate(true)
149 .open(&self.storage_path)
150 .map_err(|e| {
151 Error::Storage(format!("Failed to create storage file: {}", e))
152 })?;
153
154 let writer = BufWriter::new(file);
155 let roles = self.roles.read().unwrap();
156 serde_json::to_writer_pretty(writer, &*roles)?;
157 Ok(())
158 }
159
160 pub fn storage_path(&self) -> &Path {
162 &self.storage_path
163 }
164
165 pub fn role_count(&self) -> usize {
167 self.roles.read().unwrap().len()
168 }
169 }
170
171 impl Storage for FileStorage {
172 fn store_role(&mut self, role: Role) -> Result<()> {
173 let name = role.name().to_string();
174 self.roles.write().unwrap().insert(name, role);
175 self.save_to_disk()
176 }
177
178 fn get_role(&self, name: &str) -> Result<Option<Role>> {
179 Ok(self.roles.read().unwrap().get(name).cloned())
180 }
181
182 fn role_exists(&self, name: &str) -> Result<bool> {
183 Ok(self.roles.read().unwrap().contains_key(name))
184 }
185
186 fn delete_role(&mut self, name: &str) -> Result<bool> {
187 let removed = self.roles.write().unwrap().remove(name).is_some();
188 if removed {
189 self.save_to_disk()?;
190 }
191 Ok(removed)
192 }
193
194 fn list_roles(&self) -> Result<Vec<String>> {
195 Ok(self.roles.read().unwrap().keys().cloned().collect())
196 }
197
198 fn update_role(&mut self, role: Role) -> Result<()> {
199 let name = role.name().to_string();
200 self.roles.write().unwrap().insert(name, role);
201 self.save_to_disk()
202 }
203 }
204}
205
206#[cfg(feature = "persistence")]
207pub use file_storage::FileStorage;
208
209pub struct CompositeStorage {
211 primary: Box<dyn Storage>,
212 secondary: Option<Box<dyn Storage>>,
213}
214
215impl CompositeStorage {
216 pub fn new(primary: Box<dyn Storage>) -> Self {
218 Self {
219 primary,
220 secondary: None,
221 }
222 }
223
224 pub fn with_secondary(mut self, secondary: Box<dyn Storage>) -> Self {
226 self.secondary = Some(secondary);
227 self
228 }
229}
230
231impl Storage for CompositeStorage {
232 fn store_role(&mut self, role: Role) -> Result<()> {
233 self.primary.store_role(role.clone())?;
235
236 if let Some(secondary) = &mut self.secondary {
238 secondary.store_role(role)?;
239 }
240
241 Ok(())
242 }
243
244 fn get_role(&self, name: &str) -> Result<Option<Role>> {
245 match self.primary.get_role(name)? {
247 Some(role) => Ok(Some(role)),
248 None => {
249 if let Some(secondary) = &self.secondary {
251 secondary.get_role(name)
252 } else {
253 Ok(None)
254 }
255 }
256 }
257 }
258
259 fn role_exists(&self, name: &str) -> Result<bool> {
260 if self.primary.role_exists(name)? {
261 Ok(true)
262 } else if let Some(secondary) = &self.secondary {
263 secondary.role_exists(name)
264 } else {
265 Ok(false)
266 }
267 }
268
269 fn delete_role(&mut self, name: &str) -> Result<bool> {
270 let mut deleted = false;
271
272 if self.primary.delete_role(name)? {
273 deleted = true;
274 }
275
276 if let Some(secondary) = &mut self.secondary {
277 if secondary.delete_role(name)? {
278 deleted = true;
279 }
280 }
281
282 Ok(deleted)
283 }
284
285 fn list_roles(&self) -> Result<Vec<String>> {
286 let mut roles = self.primary.list_roles()?;
287
288 if let Some(secondary) = &self.secondary {
289 let secondary_roles = secondary.list_roles()?;
290 for role in secondary_roles {
291 if !roles.contains(&role) {
292 roles.push(role);
293 }
294 }
295 }
296
297 roles.sort();
298 Ok(roles)
299 }
300
301 fn update_role(&mut self, role: Role) -> Result<()> {
302 self.primary.update_role(role.clone())?;
303
304 if let Some(secondary) = &mut self.secondary {
305 secondary.update_role(role)?;
306 }
307
308 Ok(())
309 }
310}
311
312#[cfg(test)]
313mod tests {
314 use super::*;
315 use crate::{permission::Permission, role::Role};
316
317 #[test]
318 fn test_memory_storage() {
319 let mut storage = MemoryStorage::new();
320
321 let role = Role::new("test-role")
322 .add_permission(Permission::new("read", "documents"));
323
324 storage.store_role(role.clone()).unwrap();
326 assert_eq!(storage.role_count(), 1);
327
328 assert!(storage.role_exists("test-role").unwrap());
330 assert!(!storage.role_exists("non-existent").unwrap());
331
332 let retrieved = storage.get_role("test-role").unwrap().unwrap();
334 assert_eq!(retrieved.name(), "test-role");
335
336 let roles = storage.list_roles().unwrap();
338 assert_eq!(roles.len(), 1);
339 assert!(roles.contains(&"test-role".to_string()));
340
341 assert!(storage.delete_role("test-role").unwrap());
343 assert!(!storage.role_exists("test-role").unwrap());
344 assert_eq!(storage.role_count(), 0);
345 }
346
347 #[cfg(feature = "persistence")]
348 #[test]
349 fn test_file_storage() {
350 use std::env;
351
352 let temp_dir = env::temp_dir();
353 let storage_path = temp_dir.join("test_roles.json");
354
355 let _ = std::fs::remove_file(&storage_path);
357
358 {
359 let mut storage = FileStorage::new(&storage_path).unwrap();
360
361 let role = Role::new("file-test-role")
362 .add_permission(Permission::new("read", "documents"));
363
364 storage.store_role(role.clone()).unwrap();
366 assert_eq!(storage.role_count(), 1);
367
368 assert!(storage_path.exists());
370 }
371
372 {
374 let storage = FileStorage::new(&storage_path).unwrap();
375 assert_eq!(storage.role_count(), 1);
376
377 let retrieved = storage.get_role("file-test-role").unwrap().unwrap();
378 assert_eq!(retrieved.name(), "file-test-role");
379 }
380
381 let _ = std::fs::remove_file(&storage_path);
383 }
384
385 #[test]
386 fn test_composite_storage() {
387 let primary = Box::new(MemoryStorage::new());
388 let secondary = Box::new(MemoryStorage::new());
389
390 let mut storage = CompositeStorage::new(primary).with_secondary(secondary);
391
392 let role = Role::new("composite-test")
393 .add_permission(Permission::new("read", "documents"));
394
395 storage.store_role(role.clone()).unwrap();
397
398 let retrieved = storage.get_role("composite-test").unwrap().unwrap();
400 assert_eq!(retrieved.name(), "composite-test");
401
402 let roles = storage.list_roles().unwrap();
404 assert!(roles.contains(&"composite-test".to_string()));
405 }
406}