1use std::collections::HashMap;
10use std::sync::Arc;
11use std::time::{Duration, SystemTime, UNIX_EPOCH};
12use tokio::sync::RwLock;
13use serde::{Deserialize, Serialize};
14
15#[derive(Debug, Clone)]
17pub struct BackupManager {
18 backups: Arc<RwLock<HashMap<String, Backup>>>,
20 config: BackupConfig,
22 initialized: bool,
24}
25
26impl BackupManager {
27 pub fn new() -> Self {
29 Self {
30 backups: Arc::new(RwLock::new(HashMap::new())),
31 config: BackupConfig::default(),
32 initialized: false,
33 }
34 }
35
36 pub fn with_config(config: BackupConfig) -> Self {
38 Self {
39 backups: Arc::new(RwLock::new(HashMap::new())),
40 config,
41 initialized: false,
42 }
43 }
44
45 pub async fn initialize(&mut self) -> Result<(), BackupError> {
47 self.initialized = true;
48 Ok(())
49 }
50
51 pub async fn shutdown(&mut self) -> Result<(), BackupError> {
53 self.initialized = false;
54 Ok(())
55 }
56
57 pub fn is_initialized(&self) -> bool {
59 self.initialized
60 }
61
62 pub async fn create_backup(&self, data: &[u8], metadata: &BackupMetadata) -> Result<BackupResult, BackupError> {
64 if !self.initialized {
65 return Err(BackupError::NotInitialized);
66 }
67
68 let backup_id = format!("backup_{}", SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs());
69
70 let backup = Backup {
71 id: backup_id.clone(),
72 data: data.to_vec(),
73 metadata: metadata.clone(),
74 created_at: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(),
75 size: data.len(),
76 checksum: self.calculate_checksum(data).await?,
77 };
78
79 let mut backups = self.backups.write().await;
81 backups.insert(backup_id.clone(), backup);
82
83 Ok(BackupResult {
84 backup_id,
85 size: data.len(),
86 created_at: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(),
87 })
88 }
89
90 pub async fn restore_backup(&self, backup_id: &str) -> Result<RestoreResult, BackupError> {
92 if !self.initialized {
93 return Err(BackupError::NotInitialized);
94 }
95
96 let backups = self.backups.read().await;
97 let backup = backups.get(backup_id)
98 .ok_or_else(|| BackupError::BackupNotFound(backup_id.to_string()))?;
99
100 let calculated_checksum = self.calculate_checksum(&backup.data).await?;
102 if calculated_checksum != backup.checksum {
103 return Err(BackupError::BackupCorrupted(backup_id.to_string()));
104 }
105
106 Ok(RestoreResult {
107 backup_id: backup_id.to_string(),
108 data: backup.data.clone(),
109 metadata: backup.metadata.clone(),
110 restored_at: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(),
111 })
112 }
113
114 pub async fn list_backups(&self) -> Result<Vec<BackupInfo>, BackupError> {
116 if !self.initialized {
117 return Err(BackupError::NotInitialized);
118 }
119
120 let backups = self.backups.read().await;
121 let backup_infos: Vec<BackupInfo> = backups.values()
122 .map(|backup| BackupInfo {
123 id: backup.id.clone(),
124 size: backup.size,
125 created_at: backup.created_at,
126 metadata: backup.metadata.clone(),
127 })
128 .collect();
129
130 Ok(backup_infos)
131 }
132
133 pub async fn delete_backup(&self, backup_id: &str) -> Result<(), BackupError> {
135 if !self.initialized {
136 return Err(BackupError::NotInitialized);
137 }
138
139 let mut backups = self.backups.write().await;
140 backups.remove(backup_id)
141 .ok_or_else(|| BackupError::BackupNotFound(backup_id.to_string()))?;
142
143 Ok(())
144 }
145
146 pub async fn get_backup_info(&self, backup_id: &str) -> Result<BackupInfo, BackupError> {
148 if !self.initialized {
149 return Err(BackupError::NotInitialized);
150 }
151
152 let backups = self.backups.read().await;
153 let backup = backups.get(backup_id)
154 .ok_or_else(|| BackupError::BackupNotFound(backup_id.to_string()))?;
155
156 Ok(BackupInfo {
157 id: backup.id.clone(),
158 size: backup.size,
159 created_at: backup.created_at,
160 metadata: backup.metadata.clone(),
161 })
162 }
163
164 pub async fn verify_backup(&self, backup_id: &str) -> Result<bool, BackupError> {
166 if !self.initialized {
167 return Err(BackupError::NotInitialized);
168 }
169
170 let backups = self.backups.read().await;
171 let backup = backups.get(backup_id)
172 .ok_or_else(|| BackupError::BackupNotFound(backup_id.to_string()))?;
173
174 let calculated_checksum = self.calculate_checksum(&backup.data).await?;
175 Ok(calculated_checksum == backup.checksum)
176 }
177
178 async fn calculate_checksum(&self, data: &[u8]) -> Result<String, BackupError> {
180 use sha2::{Sha256, Digest};
181
182 let mut hasher = Sha256::new();
183 hasher.update(data);
184 let result = hasher.finalize();
185 Ok(format!("{:x}", result))
186 }
187}
188
189#[derive(Debug, Clone)]
191pub struct RestoreManager {
192 restores: Arc<RwLock<HashMap<String, RestoreOperation>>>,
194 initialized: bool,
196}
197
198impl RestoreManager {
199 pub fn new() -> Self {
201 Self {
202 restores: Arc::new(RwLock::new(HashMap::new())),
203 initialized: false,
204 }
205 }
206
207 pub async fn initialize(&mut self) -> Result<(), BackupError> {
209 self.initialized = true;
210 Ok(())
211 }
212
213 pub async fn shutdown(&mut self) -> Result<(), BackupError> {
215 self.initialized = false;
216 Ok(())
217 }
218
219 pub fn is_initialized(&self) -> bool {
221 self.initialized
222 }
223
224 pub async fn start_restore(&self, backup_id: &str, strategy: RestoreStrategy) -> Result<String, BackupError> {
226 if !self.initialized {
227 return Err(BackupError::NotInitialized);
228 }
229
230 let restore_id = format!("restore_{}", SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs());
231
232 let restore_operation = RestoreOperation {
233 id: restore_id.clone(),
234 backup_id: backup_id.to_string(),
235 strategy,
236 status: RestoreStatus::InProgress,
237 started_at: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(),
238 completed_at: None,
239 error: None,
240 };
241
242 let mut restores = self.restores.write().await;
243 restores.insert(restore_id.clone(), restore_operation);
244
245 Ok(restore_id)
246 }
247
248 pub async fn get_restore_status(&self, restore_id: &str) -> Result<RestoreOperation, BackupError> {
250 if !self.initialized {
251 return Err(BackupError::NotInitialized);
252 }
253
254 let restores = self.restores.read().await;
255 let restore = restores.get(restore_id)
256 .ok_or_else(|| BackupError::RestoreNotFound(restore_id.to_string()))?;
257
258 Ok(restore.clone())
259 }
260
261 pub async fn complete_restore(&self, restore_id: &str, success: bool, error: Option<String>) -> Result<(), BackupError> {
263 if !self.initialized {
264 return Err(BackupError::NotInitialized);
265 }
266
267 let mut restores = self.restores.write().await;
268 let restore = restores.get_mut(restore_id)
269 .ok_or_else(|| BackupError::RestoreNotFound(restore_id.to_string()))?;
270
271 restore.status = if success {
272 RestoreStatus::Completed
273 } else {
274 RestoreStatus::Failed
275 };
276 restore.completed_at = Some(SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs());
277 restore.error = error;
278
279 Ok(())
280 }
281}
282
283#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
285pub struct Backup {
286 pub id: String,
288 pub data: Vec<u8>,
290 pub metadata: BackupMetadata,
292 pub created_at: u64,
294 pub size: usize,
296 pub checksum: String,
298}
299
300#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
302pub struct BackupMetadata {
303 pub name: String,
305 pub description: String,
307 pub backup_type: BackupType,
309 pub source: String,
311 pub tags: HashMap<String, String>,
313}
314
315#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
317pub enum BackupType {
318 Full,
320 Incremental,
322 Differential,
324}
325
326#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
328pub enum BackupStrategy {
329 Automatic,
331 Manual,
333 Scheduled,
335}
336
337#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
339pub enum RestoreStrategy {
340 Full,
342 Partial,
344 PointInTime,
346}
347
348#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
350pub struct BackupResult {
351 pub backup_id: String,
353 pub size: usize,
355 pub created_at: u64,
357}
358
359#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
361pub struct RestoreResult {
362 pub backup_id: String,
364 pub data: Vec<u8>,
366 pub metadata: BackupMetadata,
368 pub restored_at: u64,
370}
371
372#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
374pub struct BackupInfo {
375 pub id: String,
377 pub size: usize,
379 pub created_at: u64,
381 pub metadata: BackupMetadata,
383}
384
385#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
387pub struct RestoreOperation {
388 pub id: String,
390 pub backup_id: String,
392 pub strategy: RestoreStrategy,
394 pub status: RestoreStatus,
396 pub started_at: u64,
398 pub completed_at: Option<u64>,
400 pub error: Option<String>,
402}
403
404#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
406pub enum RestoreStatus {
407 InProgress,
409 Completed,
411 Failed,
413}
414
415#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
417pub struct BackupConfig {
418 pub enable_backups: bool,
420 pub retention_period: Duration,
422 pub max_backups: usize,
424 pub strategy: BackupStrategy,
426}
427
428impl Default for BackupConfig {
429 fn default() -> Self {
430 Self {
431 enable_backups: true,
432 retention_period: Duration::from_secs(7 * 24 * 60 * 60), max_backups: 10,
434 strategy: BackupStrategy::Automatic,
435 }
436 }
437}
438
439#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
441pub enum BackupError {
442 NotInitialized,
444 BackupNotFound(String),
446 RestoreNotFound(String),
448 BackupFailed(String),
450 RestoreFailed(String),
452 BackupCorrupted(String),
454 ConfigurationError(String),
456}
457
458impl std::fmt::Display for BackupError {
459 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
460 match self {
461 BackupError::NotInitialized => write!(f, "Backup system not initialized"),
462 BackupError::BackupNotFound(id) => write!(f, "Backup not found: {}", id),
463 BackupError::RestoreNotFound(id) => write!(f, "Restore not found: {}", id),
464 BackupError::BackupFailed(msg) => write!(f, "Backup failed: {}", msg),
465 BackupError::RestoreFailed(msg) => write!(f, "Restore failed: {}", msg),
466 BackupError::BackupCorrupted(id) => write!(f, "Backup corrupted: {}", id),
467 BackupError::ConfigurationError(msg) => write!(f, "Configuration error: {}", msg),
468 }
469 }
470}
471
472impl std::error::Error for BackupError {}
473
474#[cfg(test)]
475mod tests {
476 use super::*;
477
478 #[tokio::test]
479 async fn test_backup_manager_creation() {
480 let manager = BackupManager::new();
481 assert!(!manager.is_initialized());
482 }
483
484 #[tokio::test]
485 async fn test_backup_manager_initialization() {
486 let mut manager = BackupManager::new();
487 let result = manager.initialize().await;
488 assert!(result.is_ok());
489 assert!(manager.is_initialized());
490 }
491
492 #[tokio::test]
493 async fn test_backup_manager_shutdown() {
494 let mut manager = BackupManager::new();
495 manager.initialize().await.unwrap();
496 let result = manager.shutdown().await;
497 assert!(result.is_ok());
498 assert!(!manager.is_initialized());
499 }
500
501 #[tokio::test]
502 async fn test_create_backup() {
503 let mut manager = BackupManager::new();
504 manager.initialize().await.unwrap();
505
506 let data = b"Hello, World!";
507 let metadata = BackupMetadata {
508 name: "test_backup".to_string(),
509 description: "Test backup".to_string(),
510 backup_type: BackupType::Full,
511 source: "test_system".to_string(),
512 tags: HashMap::new(),
513 };
514
515 let result = manager.create_backup(data, &metadata).await.unwrap();
516 assert!(!result.backup_id.is_empty());
517 assert_eq!(result.size, data.len());
518 assert!(result.created_at > 0);
519 }
520
521 #[tokio::test]
522 async fn test_restore_backup() {
523 let mut manager = BackupManager::new();
524 manager.initialize().await.unwrap();
525
526 let data = b"Hello, World!";
527 let metadata = BackupMetadata {
528 name: "test_backup".to_string(),
529 description: "Test backup".to_string(),
530 backup_type: BackupType::Full,
531 source: "test_system".to_string(),
532 tags: HashMap::new(),
533 };
534
535 let backup_result = manager.create_backup(data, &metadata).await.unwrap();
536 let restore_result = manager.restore_backup(&backup_result.backup_id).await.unwrap();
537
538 assert_eq!(restore_result.backup_id, backup_result.backup_id);
539 assert_eq!(restore_result.data, data);
540 assert_eq!(restore_result.metadata, metadata);
541 }
542
543 #[tokio::test]
544 async fn test_list_backups() {
545 let mut manager = BackupManager::new();
546 manager.initialize().await.unwrap();
547
548 let data = b"Hello, World!";
549 let metadata = BackupMetadata {
550 name: "test_backup".to_string(),
551 description: "Test backup".to_string(),
552 backup_type: BackupType::Full,
553 source: "test_system".to_string(),
554 tags: HashMap::new(),
555 };
556
557 manager.create_backup(data, &metadata).await.unwrap();
558
559 let backups = manager.list_backups().await.unwrap();
560 assert_eq!(backups.len(), 1);
561 assert_eq!(backups[0].metadata.name, "test_backup");
562 }
563
564 #[tokio::test]
565 async fn test_delete_backup() {
566 let mut manager = BackupManager::new();
567 manager.initialize().await.unwrap();
568
569 let data = b"Hello, World!";
570 let metadata = BackupMetadata {
571 name: "test_backup".to_string(),
572 description: "Test backup".to_string(),
573 backup_type: BackupType::Full,
574 source: "test_system".to_string(),
575 tags: HashMap::new(),
576 };
577
578 let backup_result = manager.create_backup(data, &metadata).await.unwrap();
579
580 let result = manager.delete_backup(&backup_result.backup_id).await;
581 assert!(result.is_ok());
582
583 let backups = manager.list_backups().await.unwrap();
584 assert_eq!(backups.len(), 0);
585 }
586
587 #[tokio::test]
588 async fn test_get_backup_info() {
589 let mut manager = BackupManager::new();
590 manager.initialize().await.unwrap();
591
592 let data = b"Hello, World!";
593 let metadata = BackupMetadata {
594 name: "test_backup".to_string(),
595 description: "Test backup".to_string(),
596 backup_type: BackupType::Full,
597 source: "test_system".to_string(),
598 tags: HashMap::new(),
599 };
600
601 let backup_result = manager.create_backup(data, &metadata).await.unwrap();
602 let backup_info = manager.get_backup_info(&backup_result.backup_id).await.unwrap();
603
604 assert_eq!(backup_info.id, backup_result.backup_id);
605 assert_eq!(backup_info.size, data.len());
606 assert_eq!(backup_info.metadata.name, "test_backup");
607 }
608
609 #[tokio::test]
610 async fn test_verify_backup() {
611 let mut manager = BackupManager::new();
612 manager.initialize().await.unwrap();
613
614 let data = b"Hello, World!";
615 let metadata = BackupMetadata {
616 name: "test_backup".to_string(),
617 description: "Test backup".to_string(),
618 backup_type: BackupType::Full,
619 source: "test_system".to_string(),
620 tags: HashMap::new(),
621 };
622
623 let backup_result = manager.create_backup(data, &metadata).await.unwrap();
624 let is_valid = manager.verify_backup(&backup_result.backup_id).await.unwrap();
625
626 assert!(is_valid);
627 }
628
629 #[tokio::test]
630 async fn test_restore_manager() {
631 let mut restore_manager = RestoreManager::new();
632 restore_manager.initialize().await.unwrap();
633
634 let restore_id = restore_manager.start_restore("test_backup", RestoreStrategy::Full).await.unwrap();
635 assert!(!restore_id.is_empty());
636
637 let restore_operation = restore_manager.get_restore_status(&restore_id).await.unwrap();
638 assert_eq!(restore_operation.id, restore_id);
639 assert_eq!(restore_operation.backup_id, "test_backup");
640 assert_eq!(restore_operation.status, RestoreStatus::InProgress);
641
642 restore_manager.complete_restore(&restore_id, true, None).await.unwrap();
643
644 let restore_operation = restore_manager.get_restore_status(&restore_id).await.unwrap();
645 assert_eq!(restore_operation.status, RestoreStatus::Completed);
646 assert!(restore_operation.completed_at.is_some());
647 }
648
649 #[tokio::test]
650 async fn test_backup_not_found() {
651 let mut manager = BackupManager::new();
652 manager.initialize().await.unwrap();
653
654 let result = manager.restore_backup("nonexistent").await;
655 assert!(result.is_err());
656 assert!(matches!(result.unwrap_err(), BackupError::BackupNotFound(_)));
657 }
658
659 #[tokio::test]
660 async fn test_restore_not_found() {
661 let mut restore_manager = RestoreManager::new();
662 restore_manager.initialize().await.unwrap();
663
664 let result = restore_manager.get_restore_status("nonexistent").await;
665 assert!(result.is_err());
666 assert!(matches!(result.unwrap_err(), BackupError::RestoreNotFound(_)));
667 }
668
669 #[test]
670 fn test_backup_config_default() {
671 let config = BackupConfig::default();
672 assert!(config.enable_backups);
673 assert_eq!(config.retention_period, Duration::from_secs(7 * 24 * 60 * 60));
674 assert_eq!(config.max_backups, 10);
675 assert_eq!(config.strategy, BackupStrategy::Automatic);
676 }
677
678 #[test]
679 fn test_backup_metadata_creation() {
680 let metadata = BackupMetadata {
681 name: "test_backup".to_string(),
682 description: "Test backup".to_string(),
683 backup_type: BackupType::Full,
684 source: "test_system".to_string(),
685 tags: HashMap::new(),
686 };
687
688 assert_eq!(metadata.name, "test_backup");
689 assert_eq!(metadata.description, "Test backup");
690 assert_eq!(metadata.backup_type, BackupType::Full);
691 assert_eq!(metadata.source, "test_system");
692 }
693
694 #[test]
695 fn test_backup_error_display() {
696 let error = BackupError::BackupFailed("Test error".to_string());
697 let error_string = format!("{}", error);
698 assert!(error_string.contains("Backup failed"));
699 assert!(error_string.contains("Test error"));
700 }
701}