//! 状态迁移和升级数据模块 use serde::{Deserialize, Serialize}; use std::collections::HashMap; /// 升级数据 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UpgradeData { /// 迁移脚本(可选,二进制格式) pub migration_script: Option>, /// 配置变更 pub config_changes: HashMap, /// 状态迁移列表 pub state_migrations: Vec, /// 破坏性变更列表 pub breaking_changes: Vec, } impl UpgradeData { pub fn new() -> Self { Self { migration_script: None, config_changes: HashMap::new(), state_migrations: Vec::new(), breaking_changes: Vec::new(), } } /// 添加配置变更 pub fn add_config_change(&mut self, key: String, value: String) { self.config_changes.insert(key, value); } /// 添加状态迁移 pub fn add_state_migration(&mut self, migration: StateMigration) { self.state_migrations.push(migration); } /// 添加破坏性变更 pub fn add_breaking_change(&mut self, change: String) { self.breaking_changes.push(change); } /// 检查是否有破坏性变更 pub fn has_breaking_changes(&self) -> bool { !self.breaking_changes.is_empty() } /// 检查是否有状态迁移 pub fn has_state_migrations(&self) -> bool { !self.state_migrations.is_empty() } } impl Default for UpgradeData { fn default() -> Self { Self::new() } } /// 状态迁移 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StateMigration { /// 迁移ID pub migration_id: String, /// 源schema版本 pub from_schema: String, /// 目标schema版本 pub to_schema: String, /// 迁移函数名称 pub migration_fn: String, /// 迁移描述 pub description: String, /// 是否可回滚 pub reversible: bool, } impl StateMigration { pub fn new( migration_id: String, from_schema: String, to_schema: String, migration_fn: String, description: String, reversible: bool, ) -> Self { Self { migration_id, from_schema, to_schema, migration_fn, description, reversible, } } } /// 迁移脚本 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MigrationScript { /// 脚本ID pub script_id: String, /// 脚本名称 pub name: String, /// 脚本内容(可能是Rust代码、SQL等) pub content: String, /// 脚本类型(rust, sql, shell等) pub script_type: ScriptType, /// 执行顺序 pub execution_order: u32, } impl MigrationScript { pub fn new( script_id: String, name: String, content: String, script_type: ScriptType, execution_order: u32, ) -> Self { Self { script_id, name, content, script_type, execution_order, } } } /// 脚本类型 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum ScriptType { /// Rust代码 Rust, /// SQL脚本 Sql, /// Shell脚本 Shell, /// Python脚本 Python, /// 自定义 Custom(String), } /// 迁移执行器 pub struct MigrationExecutor { scripts: Vec, } impl MigrationExecutor { pub fn new() -> Self { Self { scripts: Vec::new(), } } /// 添加迁移脚本 pub fn add_script(&mut self, script: MigrationScript) { self.scripts.push(script); } /// 按执行顺序排序脚本 pub fn sort_scripts(&mut self) { self.scripts.sort_by_key(|s| s.execution_order); } /// 获取所有脚本 pub fn get_scripts(&self) -> &[MigrationScript] { &self.scripts } /// 执行所有迁移脚本(模拟) /// 实际实现需要根据script_type调用不同的执行器 pub fn execute_all(&self) -> Result<(), String> { for script in &self.scripts { // 这里只是模拟,实际需要根据script_type执行 log::info!("Executing migration script: {}", script.name); } Ok(()) } } impl Default for MigrationExecutor { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_upgrade_data_creation() { let data = UpgradeData::new(); assert!(data.config_changes.is_empty()); assert!(data.state_migrations.is_empty()); assert!(data.breaking_changes.is_empty()); } #[test] fn test_upgrade_data_add_config_change() { let mut data = UpgradeData::new(); data.add_config_change("max_block_size".to_string(), "2MB".to_string()); assert_eq!(data.config_changes.get("max_block_size"), Some(&"2MB".to_string())); } #[test] fn test_upgrade_data_add_state_migration() { let mut data = UpgradeData::new(); let migration = StateMigration::new( "mig001".to_string(), "v1".to_string(), "v2".to_string(), "migrate_v1_to_v2".to_string(), "Migrate from v1 to v2".to_string(), true, ); data.add_state_migration(migration); assert_eq!(data.state_migrations.len(), 1); assert!(data.has_state_migrations()); } #[test] fn test_upgrade_data_add_breaking_change() { let mut data = UpgradeData::new(); data.add_breaking_change("Removed deprecated API".to_string()); assert_eq!(data.breaking_changes.len(), 1); assert!(data.has_breaking_changes()); } #[test] fn test_state_migration_creation() { let migration = StateMigration::new( "mig001".to_string(), "v1".to_string(), "v2".to_string(), "migrate_v1_to_v2".to_string(), "Migrate from v1 to v2".to_string(), true, ); assert_eq!(migration.migration_id, "mig001"); assert_eq!(migration.from_schema, "v1"); assert_eq!(migration.to_schema, "v2"); assert!(migration.reversible); } #[test] fn test_migration_script_creation() { let script = MigrationScript::new( "script001".to_string(), "init_db".to_string(), "CREATE TABLE ...".to_string(), ScriptType::Sql, 1, ); assert_eq!(script.script_id, "script001"); assert_eq!(script.name, "init_db"); assert_eq!(script.script_type, ScriptType::Sql); assert_eq!(script.execution_order, 1); } #[test] fn test_migration_executor() { let mut executor = MigrationExecutor::new(); let script1 = MigrationScript::new( "s1".to_string(), "script1".to_string(), "content1".to_string(), ScriptType::Rust, 2, ); let script2 = MigrationScript::new( "s2".to_string(), "script2".to_string(), "content2".to_string(), ScriptType::Rust, 1, ); executor.add_script(script1); executor.add_script(script2); executor.sort_scripts(); let scripts = executor.get_scripts(); assert_eq!(scripts.len(), 2); assert_eq!(scripts[0].execution_order, 1); assert_eq!(scripts[1].execution_order, 2); } #[test] fn test_script_type() { assert_eq!(ScriptType::Rust, ScriptType::Rust); assert_ne!(ScriptType::Rust, ScriptType::Sql); let custom = ScriptType::Custom("wasm".to_string()); assert!(matches!(custom, ScriptType::Custom(_))); } }