turbovault_core/
multi_vault.rs1use crate::prelude::*;
10use std::collections::HashMap;
11use std::sync::Arc;
12use tokio::sync::RwLock;
13
14#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
16pub struct VaultInfo {
17 pub name: String,
19 pub path: std::path::PathBuf,
21 pub is_default: bool,
23 pub config: VaultConfig,
25}
26
27pub struct MultiVaultManager {
29 vaults: Arc<RwLock<HashMap<String, VaultConfig>>>,
31 default_vault: Arc<RwLock<String>>,
33 config: ServerConfig,
35}
36
37impl MultiVaultManager {
38 pub fn new(config: ServerConfig) -> Result<Self> {
40 let vaults = Arc::new(RwLock::new(
42 config
43 .vaults
44 .iter()
45 .map(|v| (v.name.clone(), v.clone()))
46 .collect(),
47 ));
48
49 let default_name = if config.vaults.is_empty() {
51 String::new() } else {
53 config
54 .vaults
55 .iter()
56 .find(|v| v.is_default)
57 .map(|v| v.name.clone())
58 .or_else(|| config.vaults.first().map(|v| v.name.clone()))
59 .unwrap_or_default()
60 };
61
62 Ok(Self {
63 vaults,
64 default_vault: Arc::new(RwLock::new(default_name)),
65 config,
66 })
67 }
68
69 pub fn empty(config: ServerConfig) -> Result<Self> {
71 Ok(Self {
73 vaults: Arc::new(RwLock::new(HashMap::new())),
74 default_vault: Arc::new(RwLock::new(String::new())),
75 config,
76 })
77 }
78
79 pub async fn add_vault(&self, vault_config: VaultConfig) -> Result<()> {
81 let mut vaults = self.vaults.write().await;
82
83 if vaults.contains_key(&vault_config.name) {
84 return Err(Error::invalid_path(format!(
85 "Vault '{}' already exists",
86 vault_config.name
87 )));
88 }
89
90 let is_first_vault = vaults.is_empty();
91 vaults.insert(vault_config.name.clone(), vault_config.clone());
92
93 if is_first_vault {
95 drop(vaults); *self.default_vault.write().await = vault_config.name;
97 }
98
99 Ok(())
100 }
101
102 pub async fn remove_vault(&self, name: &str) -> Result<()> {
104 let mut vaults = self.vaults.write().await;
105
106 if !vaults.contains_key(name) {
107 return Err(Error::not_found(format!("Vault '{}' not found", name)));
108 }
109
110 let current_default = self.default_vault.read().await;
111
112 if *current_default == name {
114 drop(current_default); vaults.remove(name);
116
117 if let Some((first_name, _)) = vaults.iter().next() {
119 *self.default_vault.write().await = first_name.clone();
120 } else {
121 *self.default_vault.write().await = String::new();
122 }
123 } else {
124 vaults.remove(name);
125 }
126
127 Ok(())
128 }
129
130 pub async fn get_vault_config(&self, name: &str) -> Result<VaultConfig> {
132 let vaults = self.vaults.read().await;
133 vaults
134 .get(name)
135 .cloned()
136 .ok_or_else(|| Error::not_found(format!("Vault '{}' not found", name)))
137 }
138
139 pub async fn get_active_vault(&self) -> String {
141 self.default_vault.read().await.clone()
142 }
143
144 pub async fn set_active_vault(&self, name: &str) -> Result<()> {
146 let vaults = self.vaults.read().await;
147
148 if !vaults.contains_key(name) {
149 return Err(Error::not_found(format!("Vault '{}' not found", name)));
150 }
151
152 *self.default_vault.write().await = name.to_string();
153 Ok(())
154 }
155
156 pub async fn list_vaults(&self) -> Result<Vec<VaultInfo>> {
158 let vaults = self.vaults.read().await;
159 let default = self.default_vault.read().await.clone();
160
161 let infos = vaults
162 .iter()
163 .map(|(name, config)| VaultInfo {
164 name: name.clone(),
165 path: config.path.clone(),
166 is_default: name == &default,
167 config: config.clone(),
168 })
169 .collect();
170
171 Ok(infos)
172 }
173
174 pub async fn get_effective_vault_settings(&self, vault_name: &str) -> Result<VaultConfig> {
176 let vault_config = self.get_vault_config(vault_name).await?;
177
178 let effective = vault_config.clone();
180
181 Ok(effective)
185 }
186
187 pub async fn vault_count(&self) -> usize {
189 self.vaults.read().await.len()
190 }
191
192 pub async fn vault_exists(&self, name: &str) -> bool {
194 self.vaults.read().await.contains_key(name)
195 }
196
197 pub async fn get_active_vault_config(&self) -> Result<VaultConfig> {
199 let active_name = self.default_vault.read().await.clone();
200 if active_name.is_empty() {
201 return Err(Error::not_found(
202 "No vault is currently active. Please add a vault using add_vault tool."
203 .to_string(),
204 ));
205 }
206 self.get_vault_config(&active_name).await
207 }
208}
209
210impl Clone for MultiVaultManager {
211 fn clone(&self) -> Self {
212 Self {
213 vaults: self.vaults.clone(),
214 default_vault: self.default_vault.clone(),
215 config: self.config.clone(),
216 }
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223
224 fn create_test_vault(name: &str, is_default: bool) -> VaultConfig {
225 VaultConfig {
226 name: name.to_string(),
227 path: std::path::PathBuf::from(format!("/tmp/{}", name)),
228 is_default,
229 watch_for_changes: None,
230 max_file_size: None,
231 allowed_extensions: None,
232 excluded_paths: None,
233 enable_caching: None,
234 cache_ttl: None,
235 template_dirs: None,
236 allowed_operations: None,
237 }
238 }
239
240 fn create_test_config() -> ServerConfig {
241 let mut config = ServerConfig::new();
242 config.vaults = vec![
243 create_test_vault("vault1", true),
244 create_test_vault("vault2", false),
245 ];
246 config
247 }
248
249 #[test]
250 fn test_multi_vault_manager_creation() {
251 let config = create_test_config();
252 let manager = MultiVaultManager::new(config);
253 assert!(manager.is_ok());
254 }
255
256 #[test]
257 fn test_can_create_empty_vaults() {
258 let config = ServerConfig::new(); let manager = MultiVaultManager::new(config);
261 assert!(manager.is_ok());
262 let mgr = manager.unwrap();
263 let rt = tokio::runtime::Runtime::new().unwrap();
265 let default = rt.block_on(async { mgr.get_active_vault().await });
266 assert!(default.is_empty());
267 }
268
269 #[tokio::test]
270 async fn test_get_active_vault() {
271 let config = create_test_config();
272 let manager = MultiVaultManager::new(config).unwrap();
273 let active = manager.get_active_vault().await;
274 assert_eq!(active, "vault1");
275 }
276
277 #[tokio::test]
278 async fn test_set_active_vault() {
279 let config = create_test_config();
280 let manager = MultiVaultManager::new(config).unwrap();
281
282 manager.set_active_vault("vault2").await.unwrap();
283 let active = manager.get_active_vault().await;
284 assert_eq!(active, "vault2");
285 }
286
287 #[tokio::test]
288 async fn test_set_invalid_active_vault() {
289 let config = create_test_config();
290 let manager = MultiVaultManager::new(config).unwrap();
291
292 let result = manager.set_active_vault("nonexistent").await;
293 assert!(result.is_err());
294 }
295
296 #[tokio::test]
297 async fn test_add_vault() {
298 let config = create_test_config();
299 let manager = MultiVaultManager::new(config).unwrap();
300
301 let new_vault = create_test_vault("vault3", false);
302 manager.add_vault(new_vault).await.unwrap();
303
304 assert!(manager.vault_exists("vault3").await);
305 }
306
307 #[tokio::test]
308 async fn test_add_duplicate_vault_fails() {
309 let config = create_test_config();
310 let manager = MultiVaultManager::new(config).unwrap();
311
312 let dup_vault = create_test_vault("vault1", false);
313 let result = manager.add_vault(dup_vault).await;
314 assert!(result.is_err());
315 }
316
317 #[tokio::test]
318 async fn test_remove_vault() {
319 let config = create_test_config();
320 let manager = MultiVaultManager::new(config).unwrap();
321
322 manager.remove_vault("vault2").await.unwrap();
323 assert!(!manager.vault_exists("vault2").await);
324 }
325
326 #[tokio::test]
327 async fn test_remove_default_vault_reassigns() {
328 let config = create_test_config();
330 let manager = MultiVaultManager::new(config).unwrap();
331
332 assert_eq!(manager.get_active_vault().await, "vault1");
334
335 let result = manager.remove_vault("vault1").await;
336 assert!(result.is_ok());
337 assert!(!manager.vault_exists("vault1").await);
338
339 let new_default = manager.get_active_vault().await;
341 assert_eq!(new_default, "vault2");
342 }
343
344 #[tokio::test]
345 async fn test_list_vaults() {
346 let config = create_test_config();
347 let manager = MultiVaultManager::new(config).unwrap();
348
349 let vaults = manager.list_vaults().await.unwrap();
350 assert_eq!(vaults.len(), 2);
351 assert!(vaults.iter().any(|v| v.name == "vault1" && v.is_default));
352 assert!(vaults.iter().any(|v| v.name == "vault2" && !v.is_default));
353 }
354
355 #[tokio::test]
356 async fn test_vault_count() {
357 let config = create_test_config();
358 let manager = MultiVaultManager::new(config).unwrap();
359
360 assert_eq!(manager.vault_count().await, 2);
361
362 manager
363 .add_vault(create_test_vault("vault3", false))
364 .await
365 .ok();
366 assert_eq!(manager.vault_count().await, 3);
367 }
368
369 #[tokio::test]
370 async fn test_get_effective_vault_settings() {
371 let config = create_test_config();
372 let manager = MultiVaultManager::new(config).unwrap();
373
374 let settings = manager
375 .get_effective_vault_settings("vault1")
376 .await
377 .unwrap();
378 assert_eq!(settings.name, "vault1");
379 }
380
381 #[tokio::test]
382 async fn test_clone() {
383 let config = create_test_config();
384 let manager = MultiVaultManager::new(config).unwrap();
385 let manager2 = manager.clone();
386
387 assert_eq!(manager.vault_count().await, manager2.vault_count().await);
388 }
389}