169 lines
5.5 KiB
Rust
169 lines
5.5 KiB
Rust
//! 升级实现助手模块
|
||
//!
|
||
//! 提供通用的升级实现辅助函数和宏
|
||
|
||
/// 为模块生成基础的Upgradeable实现
|
||
#[macro_export]
|
||
macro_rules! impl_upgradeable {
|
||
($struct_name:ident, $module_name:expr, $initial_version:expr) => {
|
||
impl $crate::traits::Upgradeable for $struct_name {
|
||
fn module_name(&self) -> &str {
|
||
$module_name
|
||
}
|
||
|
||
fn current_version(&self) -> $crate::Version {
|
||
self.version.clone()
|
||
}
|
||
|
||
fn can_upgrade_to(&self, target: &$crate::Version) -> $crate::Result<bool> {
|
||
if !self.version.can_upgrade_to(target) {
|
||
return Err($crate::UpgradeError::DowngradeNotAllowed {
|
||
from: self.version.to_string(),
|
||
to: target.to_string(),
|
||
});
|
||
}
|
||
Ok(true)
|
||
}
|
||
|
||
fn upgrade(&mut self, target: $crate::Version, data: $crate::UpgradeData) -> $crate::Result<()> {
|
||
// 检查是否可以升级
|
||
self.can_upgrade_to(&target)?;
|
||
|
||
// 创建快照
|
||
let snapshot = self.create_snapshot()?;
|
||
|
||
// 执行升级
|
||
match self.do_upgrade(target.clone(), data) {
|
||
Ok(_) => {
|
||
// 记录升级历史
|
||
let record = $crate::UpgradeRecord::new(
|
||
self.upgrade_history.len() as u64 + 1,
|
||
$module_name.to_string(),
|
||
self.version.clone(),
|
||
target.clone(),
|
||
"system".to_string(),
|
||
);
|
||
let mut record = record;
|
||
record.mark_success(snapshot.snapshot_id.clone());
|
||
self.upgrade_history.push(record);
|
||
|
||
// 更新版本
|
||
self.version = target;
|
||
Ok(())
|
||
}
|
||
Err(e) => {
|
||
// 升级失败,回滚
|
||
self.rollback(snapshot)?;
|
||
Err(e)
|
||
}
|
||
}
|
||
}
|
||
|
||
fn create_snapshot(&self) -> $crate::Result<$crate::Snapshot> {
|
||
let state_data = serde_json::to_vec(&self)
|
||
.map_err(|e| $crate::UpgradeError::SerializationError(e))?;
|
||
Ok($crate::Snapshot::new(
|
||
$module_name.to_string(),
|
||
self.version.clone(),
|
||
state_data,
|
||
))
|
||
}
|
||
|
||
fn rollback(&mut self, snapshot: $crate::Snapshot) -> $crate::Result<()> {
|
||
let restored: Self = serde_json::from_slice(&snapshot.state_data)
|
||
.map_err(|e| $crate::UpgradeError::SerializationError(e))?;
|
||
*self = restored;
|
||
Ok(())
|
||
}
|
||
|
||
fn upgrade_history(&self) -> Vec<$crate::UpgradeRecord> {
|
||
self.upgrade_history.clone()
|
||
}
|
||
|
||
fn validate_state(&self) -> $crate::Result<bool> {
|
||
// 默认实现:总是返回true
|
||
// 各模块可以override这个方法
|
||
Ok(true)
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
/// 为模块添加必要的升级字段
|
||
#[macro_export]
|
||
macro_rules! add_upgrade_fields {
|
||
() => {
|
||
/// 当前版本
|
||
pub version: $crate::Version,
|
||
/// 升级历史
|
||
pub upgrade_history: Vec<$crate::UpgradeRecord>,
|
||
};
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use crate::{traits::Upgradeable, UpgradeData, UpgradeRecord, Version};
|
||
use serde::{Deserialize, Serialize};
|
||
use std::collections::HashMap;
|
||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
struct TestModule {
|
||
version: Version,
|
||
upgrade_history: Vec<UpgradeRecord>,
|
||
data: String,
|
||
}
|
||
|
||
impl TestModule {
|
||
fn new() -> Self {
|
||
Self {
|
||
version: Version::new(1, 0, 0),
|
||
upgrade_history: Vec::new(),
|
||
data: "initial".to_string(),
|
||
}
|
||
}
|
||
|
||
fn do_upgrade(&mut self, _target: Version, _data: UpgradeData) -> crate::Result<()> {
|
||
self.data = "upgraded".to_string();
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
impl_upgradeable!(TestModule, "test-module", Version::new(1, 0, 0));
|
||
|
||
#[test]
|
||
fn test_macro_generated_impl() {
|
||
let mut module = TestModule::new();
|
||
|
||
assert_eq!(module.module_name(), "test-module");
|
||
assert_eq!(module.current_version(), Version::new(1, 0, 0));
|
||
|
||
let target = Version::new(1, 1, 0);
|
||
let data = UpgradeData {
|
||
migration_script: None,
|
||
config_changes: HashMap::new(),
|
||
state_migrations: vec![],
|
||
breaking_changes: vec![],
|
||
};
|
||
|
||
assert!(module.upgrade(target.clone(), data).is_ok());
|
||
assert_eq!(module.current_version(), target);
|
||
assert_eq!(module.data, "upgraded");
|
||
assert_eq!(module.upgrade_history().len(), 1);
|
||
}
|
||
|
||
#[test]
|
||
fn test_snapshot_and_rollback() {
|
||
let mut module = TestModule::new();
|
||
module.data = "original".to_string();
|
||
|
||
let snapshot = module.create_snapshot().unwrap();
|
||
|
||
module.data = "modified".to_string();
|
||
assert_eq!(module.data, "modified");
|
||
|
||
module.rollback(snapshot).unwrap();
|
||
assert_eq!(module.data, "original");
|
||
}
|
||
}
|