1use serde::{Deserialize, Serialize};
8use std::path::PathBuf;
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
12#[serde(tag = "backend", rename_all = "lowercase")]
13pub enum StorageBackend {
14 Sqlite {
16 path: PathBuf,
18 },
19 Json {
21 path: PathBuf,
23 },
24 Memory,
26}
27
28impl Default for StorageBackend {
29 fn default() -> Self {
30 StorageBackend::Sqlite {
31 path: PathBuf::from("./data/vbr.db"),
32 }
33 }
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct SessionConfig {
39 #[serde(default = "default_true")]
41 pub enabled: bool,
42
43 #[serde(default = "default_session_timeout")]
45 pub timeout: u64,
46
47 #[serde(default)]
49 pub scoped_data: bool,
50}
51
52fn default_true() -> bool {
53 true
54}
55
56fn default_session_timeout() -> u64 {
57 3600 }
59
60impl Default for SessionConfig {
61 fn default() -> Self {
62 Self {
63 enabled: true,
64 timeout: default_session_timeout(),
65 scoped_data: false,
66 }
67 }
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct AgingConfig {
73 #[serde(default)]
75 pub enabled: bool,
76
77 #[serde(default = "default_cleanup_interval")]
79 pub cleanup_interval: u64,
80
81 #[serde(default = "default_true")]
83 pub auto_update_timestamps: bool,
84}
85
86fn default_cleanup_interval() -> u64 {
87 3600 }
89
90impl Default for AgingConfig {
91 fn default() -> Self {
92 Self {
93 enabled: false,
94 cleanup_interval: default_cleanup_interval(),
95 auto_update_timestamps: true,
96 }
97 }
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct AuthConfig {
103 #[serde(default)]
105 pub enabled: bool,
106
107 #[serde(default)]
109 pub jwt_secret: Option<String>,
110
111 #[serde(default = "default_token_expiration")]
113 pub token_expiration: u64,
114}
115
116fn default_token_expiration() -> u64 {
117 86400 }
119
120impl Default for AuthConfig {
121 fn default() -> Self {
122 Self {
123 enabled: false,
124 jwt_secret: None,
125 token_expiration: default_token_expiration(),
126 }
127 }
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct VbrConfig {
133 #[serde(default)]
135 pub storage: StorageBackend,
136
137 #[serde(default)]
139 pub sessions: SessionConfig,
140
141 #[serde(default)]
143 pub aging: AgingConfig,
144
145 #[serde(default)]
147 pub auth: AuthConfig,
148
149 #[serde(default = "default_api_prefix")]
151 pub api_prefix: String,
152}
153
154fn default_api_prefix() -> String {
155 "/api".to_string()
156}
157
158impl Default for VbrConfig {
159 fn default() -> Self {
160 Self {
161 storage: StorageBackend::default(),
162 sessions: SessionConfig::default(),
163 aging: AgingConfig::default(),
164 auth: AuthConfig::default(),
165 api_prefix: default_api_prefix(),
166 }
167 }
168}
169
170impl VbrConfig {
171 pub fn new() -> Self {
173 Self::default()
174 }
175
176 pub fn with_storage_backend(mut self, backend: StorageBackend) -> Self {
178 self.storage = backend;
179 self
180 }
181
182 pub fn with_sessions(mut self, config: SessionConfig) -> Self {
184 self.sessions = config;
185 self
186 }
187
188 pub fn with_aging(mut self, config: AgingConfig) -> Self {
190 self.aging = config;
191 self
192 }
193
194 pub fn with_auth(mut self, config: AuthConfig) -> Self {
196 self.auth = config;
197 self
198 }
199
200 pub fn with_api_prefix(mut self, prefix: String) -> Self {
202 self.api_prefix = prefix;
203 self
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
213 fn test_storage_backend_default() {
214 let backend = StorageBackend::default();
215 assert!(matches!(backend, StorageBackend::Sqlite { .. }));
216 }
217
218 #[test]
219 fn test_storage_backend_sqlite() {
220 let backend = StorageBackend::Sqlite {
221 path: PathBuf::from("/tmp/test.db"),
222 };
223 if let StorageBackend::Sqlite { path } = backend {
224 assert_eq!(path, PathBuf::from("/tmp/test.db"));
225 } else {
226 panic!("Expected Sqlite variant");
227 }
228 }
229
230 #[test]
231 fn test_storage_backend_json() {
232 let backend = StorageBackend::Json {
233 path: PathBuf::from("/tmp/data.json"),
234 };
235 if let StorageBackend::Json { path } = backend {
236 assert_eq!(path, PathBuf::from("/tmp/data.json"));
237 } else {
238 panic!("Expected Json variant");
239 }
240 }
241
242 #[test]
243 fn test_storage_backend_memory() {
244 let backend = StorageBackend::Memory;
245 assert!(matches!(backend, StorageBackend::Memory));
246 }
247
248 #[test]
249 fn test_storage_backend_serialize_sqlite() {
250 let backend = StorageBackend::Sqlite {
251 path: PathBuf::from("./data/vbr.db"),
252 };
253 let json = serde_json::to_string(&backend).unwrap();
254 assert!(json.contains("\"backend\":\"sqlite\""));
255 assert!(json.contains("vbr.db"));
256 }
257
258 #[test]
259 fn test_storage_backend_serialize_memory() {
260 let backend = StorageBackend::Memory;
261 let json = serde_json::to_string(&backend).unwrap();
262 assert!(json.contains("\"backend\":\"memory\""));
263 }
264
265 #[test]
266 fn test_storage_backend_clone() {
267 let backend = StorageBackend::Memory;
268 let cloned = backend.clone();
269 assert!(matches!(cloned, StorageBackend::Memory));
270 }
271
272 #[test]
273 fn test_storage_backend_debug() {
274 let backend = StorageBackend::Memory;
275 let debug = format!("{:?}", backend);
276 assert!(debug.contains("Memory"));
277 }
278
279 #[test]
280 fn test_storage_backend_eq() {
281 let b1 = StorageBackend::Memory;
282 let b2 = StorageBackend::Memory;
283 assert_eq!(b1, b2);
284
285 let b3 = StorageBackend::Sqlite {
286 path: PathBuf::from("a.db"),
287 };
288 let b4 = StorageBackend::Sqlite {
289 path: PathBuf::from("b.db"),
290 };
291 assert_ne!(b3, b4);
292 }
293
294 #[test]
296 fn test_session_config_default() {
297 let config = SessionConfig::default();
298 assert!(config.enabled);
299 assert_eq!(config.timeout, 3600);
300 assert!(!config.scoped_data);
301 }
302
303 #[test]
304 fn test_session_config_clone() {
305 let config = SessionConfig {
306 enabled: false,
307 timeout: 7200,
308 scoped_data: true,
309 };
310 let cloned = config.clone();
311 assert_eq!(config.enabled, cloned.enabled);
312 assert_eq!(config.timeout, cloned.timeout);
313 assert_eq!(config.scoped_data, cloned.scoped_data);
314 }
315
316 #[test]
317 fn test_session_config_serialize() {
318 let config = SessionConfig::default();
319 let json = serde_json::to_string(&config).unwrap();
320 assert!(json.contains("\"enabled\":true"));
321 assert!(json.contains("\"timeout\":3600"));
322 }
323
324 #[test]
325 fn test_session_config_deserialize() {
326 let json = r#"{"enabled": false, "timeout": 7200, "scoped_data": true}"#;
327 let config: SessionConfig = serde_json::from_str(json).unwrap();
328 assert!(!config.enabled);
329 assert_eq!(config.timeout, 7200);
330 assert!(config.scoped_data);
331 }
332
333 #[test]
334 fn test_session_config_debug() {
335 let config = SessionConfig::default();
336 let debug = format!("{:?}", config);
337 assert!(debug.contains("SessionConfig"));
338 }
339
340 #[test]
342 fn test_aging_config_default() {
343 let config = AgingConfig::default();
344 assert!(!config.enabled);
345 assert_eq!(config.cleanup_interval, 3600);
346 assert!(config.auto_update_timestamps);
347 }
348
349 #[test]
350 fn test_aging_config_clone() {
351 let config = AgingConfig {
352 enabled: true,
353 cleanup_interval: 1800,
354 auto_update_timestamps: false,
355 };
356 let cloned = config.clone();
357 assert_eq!(config.enabled, cloned.enabled);
358 assert_eq!(config.cleanup_interval, cloned.cleanup_interval);
359 }
360
361 #[test]
362 fn test_aging_config_serialize() {
363 let config = AgingConfig::default();
364 let json = serde_json::to_string(&config).unwrap();
365 assert!(json.contains("\"enabled\":false"));
366 assert!(json.contains("\"cleanup_interval\":3600"));
367 }
368
369 #[test]
370 fn test_aging_config_debug() {
371 let config = AgingConfig::default();
372 let debug = format!("{:?}", config);
373 assert!(debug.contains("AgingConfig"));
374 }
375
376 #[test]
378 fn test_auth_config_default() {
379 let config = AuthConfig::default();
380 assert!(!config.enabled);
381 assert!(config.jwt_secret.is_none());
382 assert_eq!(config.token_expiration, 86400);
383 }
384
385 #[test]
386 fn test_auth_config_with_secret() {
387 let config = AuthConfig {
388 enabled: true,
389 jwt_secret: Some("my-secret-key".to_string()),
390 token_expiration: 3600,
391 };
392 assert!(config.enabled);
393 assert_eq!(config.jwt_secret, Some("my-secret-key".to_string()));
394 assert_eq!(config.token_expiration, 3600);
395 }
396
397 #[test]
398 fn test_auth_config_clone() {
399 let config = AuthConfig {
400 enabled: true,
401 jwt_secret: Some("secret".to_string()),
402 token_expiration: 7200,
403 };
404 let cloned = config.clone();
405 assert_eq!(config.enabled, cloned.enabled);
406 assert_eq!(config.jwt_secret, cloned.jwt_secret);
407 }
408
409 #[test]
410 fn test_auth_config_serialize() {
411 let config = AuthConfig::default();
412 let json = serde_json::to_string(&config).unwrap();
413 assert!(json.contains("\"enabled\":false"));
414 assert!(json.contains("\"token_expiration\":86400"));
415 }
416
417 #[test]
418 fn test_auth_config_debug() {
419 let config = AuthConfig::default();
420 let debug = format!("{:?}", config);
421 assert!(debug.contains("AuthConfig"));
422 }
423
424 #[test]
426 fn test_vbr_config_default() {
427 let config = VbrConfig::default();
428 assert!(config.sessions.enabled);
429 assert_eq!(config.sessions.timeout, 3600);
430 assert!(!config.aging.enabled);
431 assert!(!config.auth.enabled);
432 assert_eq!(config.api_prefix, "/api");
433 }
434
435 #[test]
436 fn test_vbr_config_new() {
437 let config = VbrConfig::new();
438 assert_eq!(config.api_prefix, "/api");
439 }
440
441 #[test]
442 fn test_vbr_config_builder() {
443 let config = VbrConfig::new()
444 .with_api_prefix("/v1/api".to_string())
445 .with_storage_backend(StorageBackend::Memory);
446
447 assert_eq!(config.api_prefix, "/v1/api");
448 assert!(matches!(config.storage, StorageBackend::Memory));
449 }
450
451 #[test]
452 fn test_vbr_config_with_sessions() {
453 let session_config = SessionConfig {
454 enabled: false,
455 timeout: 1800,
456 scoped_data: true,
457 };
458
459 let config = VbrConfig::new().with_sessions(session_config);
460 assert!(!config.sessions.enabled);
461 assert_eq!(config.sessions.timeout, 1800);
462 assert!(config.sessions.scoped_data);
463 }
464
465 #[test]
466 fn test_vbr_config_with_aging() {
467 let aging_config = AgingConfig {
468 enabled: true,
469 cleanup_interval: 600,
470 auto_update_timestamps: false,
471 };
472
473 let config = VbrConfig::new().with_aging(aging_config);
474 assert!(config.aging.enabled);
475 assert_eq!(config.aging.cleanup_interval, 600);
476 assert!(!config.aging.auto_update_timestamps);
477 }
478
479 #[test]
480 fn test_vbr_config_with_auth() {
481 let auth_config = AuthConfig {
482 enabled: true,
483 jwt_secret: Some("test-secret".to_string()),
484 token_expiration: 3600,
485 };
486
487 let config = VbrConfig::new().with_auth(auth_config);
488 assert!(config.auth.enabled);
489 assert_eq!(config.auth.jwt_secret, Some("test-secret".to_string()));
490 }
491
492 #[test]
493 fn test_vbr_config_full_builder_chain() {
494 let config = VbrConfig::new()
495 .with_storage_backend(StorageBackend::Json {
496 path: PathBuf::from("/tmp/vbr.json"),
497 })
498 .with_sessions(SessionConfig {
499 enabled: true,
500 timeout: 7200,
501 scoped_data: true,
502 })
503 .with_aging(AgingConfig {
504 enabled: true,
505 cleanup_interval: 300,
506 auto_update_timestamps: true,
507 })
508 .with_auth(AuthConfig {
509 enabled: true,
510 jwt_secret: Some("secret".to_string()),
511 token_expiration: 86400,
512 })
513 .with_api_prefix("/v2/api".to_string());
514
515 assert!(matches!(config.storage, StorageBackend::Json { .. }));
516 assert!(config.sessions.scoped_data);
517 assert!(config.aging.enabled);
518 assert!(config.auth.enabled);
519 assert_eq!(config.api_prefix, "/v2/api");
520 }
521
522 #[test]
523 fn test_vbr_config_clone() {
524 let config = VbrConfig::new()
525 .with_api_prefix("/test".to_string())
526 .with_storage_backend(StorageBackend::Memory);
527
528 let cloned = config.clone();
529 assert_eq!(config.api_prefix, cloned.api_prefix);
530 }
531
532 #[test]
533 fn test_vbr_config_serialize() {
534 let config = VbrConfig::default();
535 let json = serde_json::to_string(&config).unwrap();
536 assert!(json.contains("\"api_prefix\":\"/api\""));
537 }
538
539 #[test]
540 fn test_vbr_config_deserialize() {
541 let json = r#"{
542 "storage": {"backend": "memory"},
543 "sessions": {"enabled": true, "timeout": 3600, "scoped_data": false},
544 "aging": {"enabled": false, "cleanup_interval": 3600, "auto_update_timestamps": true},
545 "auth": {"enabled": false, "jwt_secret": null, "token_expiration": 86400},
546 "api_prefix": "/custom"
547 }"#;
548
549 let config: VbrConfig = serde_json::from_str(json).unwrap();
550 assert!(matches!(config.storage, StorageBackend::Memory));
551 assert_eq!(config.api_prefix, "/custom");
552 }
553
554 #[test]
555 fn test_vbr_config_debug() {
556 let config = VbrConfig::default();
557 let debug = format!("{:?}", config);
558 assert!(debug.contains("VbrConfig"));
559 }
560}