//! 快照和回滚模块 use crate::version::Version; use serde::{Deserialize, Serialize}; use sha3::{Digest, Sha3_384}; use std::fmt; /// 快照ID #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct SnapshotId(pub String); impl SnapshotId { pub fn new(id: String) -> Self { Self(id) } /// 从状态数据生成快照ID(使用SHA3-384哈希) pub fn generate(module_name: &str, version: &Version, state_data: &[u8]) -> Self { let mut hasher = Sha3_384::new(); hasher.update(module_name.as_bytes()); hasher.update(version.to_string().as_bytes()); hasher.update(state_data); let hash = hasher.finalize(); Self(hex::encode(hash)) } } impl fmt::Display for SnapshotId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } /// 状态快照 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Snapshot { /// 快照ID pub snapshot_id: SnapshotId, /// 模块名称 pub module_name: String, /// 版本 pub version: Version, /// 状态数据(序列化后的二进制) pub state_data: Vec, /// 创建时间 pub created_at: chrono::DateTime, /// 元数据(可选) pub metadata: std::collections::HashMap, } impl Snapshot { pub fn new(module_name: String, version: Version, state_data: Vec) -> Self { let snapshot_id = SnapshotId::generate(&module_name, &version, &state_data); Self { snapshot_id, module_name, version, state_data, created_at: chrono::Utc::now(), metadata: std::collections::HashMap::new(), } } /// 添加元数据 pub fn add_metadata(&mut self, key: String, value: String) { self.metadata.insert(key, value); } /// 获取元数据 pub fn get_metadata(&self, key: &str) -> Option<&String> { self.metadata.get(key) } /// 验证快照完整性 pub fn verify_integrity(&self) -> bool { let expected_id = SnapshotId::generate(&self.module_name, &self.version, &self.state_data); self.snapshot_id == expected_id } /// 获取快照大小(字节) pub fn size(&self) -> usize { self.state_data.len() } } /// 快照管理器 #[derive(Debug, Default)] pub struct SnapshotManager { snapshots: std::collections::HashMap, } impl SnapshotManager { pub fn new() -> Self { Self { snapshots: std::collections::HashMap::new(), } } /// 保存快照 pub fn save(&mut self, snapshot: Snapshot) -> SnapshotId { let id = snapshot.snapshot_id.clone(); self.snapshots.insert(id.clone(), snapshot); id } /// 获取快照 pub fn get(&self, id: &SnapshotId) -> Option<&Snapshot> { self.snapshots.get(id) } /// 删除快照 pub fn delete(&mut self, id: &SnapshotId) -> Option { self.snapshots.remove(id) } /// 列出所有快照 pub fn list(&self) -> Vec<&Snapshot> { self.snapshots.values().collect() } /// 列出指定模块的快照 pub fn list_by_module(&self, module_name: &str) -> Vec<&Snapshot> { self.snapshots .values() .filter(|s| s.module_name == module_name) .collect() } /// 获取最新快照 pub fn get_latest(&self, module_name: &str) -> Option<&Snapshot> { self.list_by_module(module_name) .into_iter() .max_by_key(|s| s.created_at) } /// 清理旧快照(保留最近N个) pub fn cleanup_old_snapshots(&mut self, module_name: &str, keep_count: usize) { let mut snapshots: Vec<_> = self.list_by_module(module_name) .into_iter() .map(|s| s.snapshot_id.clone()) .collect(); // 按创建时间排序 snapshots.sort_by_key(|id| { self.snapshots.get(id).map(|s| s.created_at).unwrap_or_default() }); // 删除旧快照 if snapshots.len() > keep_count { for id in snapshots.iter().take(snapshots.len() - keep_count) { self.delete(id); } } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_snapshot_id_generation() { let id1 = SnapshotId::generate("nac-nvm", &Version::new(1, 0, 0), b"state1"); let id2 = SnapshotId::generate("nac-nvm", &Version::new(1, 0, 0), b"state1"); let id3 = SnapshotId::generate("nac-nvm", &Version::new(1, 0, 0), b"state2"); assert_eq!(id1, id2); assert_ne!(id1, id3); } #[test] fn test_snapshot_creation() { let snapshot = Snapshot::new( "nac-nvm".to_string(), Version::new(1, 0, 0), vec![1, 2, 3, 4], ); assert_eq!(snapshot.module_name, "nac-nvm"); assert_eq!(snapshot.version, Version::new(1, 0, 0)); assert_eq!(snapshot.state_data, vec![1, 2, 3, 4]); } #[test] fn test_snapshot_integrity() { let snapshot = Snapshot::new( "nac-nvm".to_string(), Version::new(1, 0, 0), vec![1, 2, 3, 4], ); assert!(snapshot.verify_integrity()); } #[test] fn test_snapshot_metadata() { let mut snapshot = Snapshot::new( "nac-nvm".to_string(), Version::new(1, 0, 0), vec![1, 2, 3, 4], ); snapshot.add_metadata("description".to_string(), "test snapshot".to_string()); assert_eq!(snapshot.get_metadata("description"), Some(&"test snapshot".to_string())); } #[test] fn test_snapshot_manager() { let mut manager = SnapshotManager::new(); let snapshot1 = Snapshot::new( "nac-nvm".to_string(), Version::new(1, 0, 0), vec![1, 2, 3], ); let id1 = manager.save(snapshot1.clone()); let snapshot2 = Snapshot::new( "nac-cbpp".to_string(), Version::new(1, 0, 0), vec![4, 5, 6], ); let id2 = manager.save(snapshot2.clone()); assert_eq!(manager.list().len(), 2); assert_eq!(manager.get(&id1).unwrap().module_name, "nac-nvm"); assert_eq!(manager.get(&id2).unwrap().module_name, "nac-cbpp"); } #[test] fn test_snapshot_manager_list_by_module() { let mut manager = SnapshotManager::new(); let snapshot1 = Snapshot::new( "nac-nvm".to_string(), Version::new(1, 0, 0), vec![1, 2, 3], ); manager.save(snapshot1); let snapshot2 = Snapshot::new( "nac-nvm".to_string(), Version::new(1, 1, 0), vec![4, 5, 6], ); manager.save(snapshot2); let snapshot3 = Snapshot::new( "nac-cbpp".to_string(), Version::new(1, 0, 0), vec![7, 8, 9], ); manager.save(snapshot3); let nvm_snapshots = manager.list_by_module("nac-nvm"); assert_eq!(nvm_snapshots.len(), 2); } #[test] fn test_snapshot_manager_get_latest() { let mut manager = SnapshotManager::new(); let snapshot1 = Snapshot::new( "nac-nvm".to_string(), Version::new(1, 0, 0), vec![1, 2, 3], ); manager.save(snapshot1); std::thread::sleep(std::time::Duration::from_millis(10)); let snapshot2 = Snapshot::new( "nac-nvm".to_string(), Version::new(1, 1, 0), vec![4, 5, 6], ); manager.save(snapshot2.clone()); let latest = manager.get_latest("nac-nvm").unwrap(); assert_eq!(latest.version, Version::new(1, 1, 0)); } #[test] fn test_snapshot_manager_delete() { let mut manager = SnapshotManager::new(); let snapshot = Snapshot::new( "nac-nvm".to_string(), Version::new(1, 0, 0), vec![1, 2, 3], ); let id = manager.save(snapshot); assert!(manager.get(&id).is_some()); manager.delete(&id); assert!(manager.get(&id).is_none()); } }