Rust插件库revision的使用:高效版本控制与代码变更管理工具
use revision::Error;
use revision::revisioned;
// 测试结构体当前版本为3
#[derive(Debug, PartialEq)]
#[revisioned(revision = 3)]
pub struct TestStruct {
a: u32,
#[revision(start = 2, end = 3, convert_fn = "convert_b")]
b: u8,
#[revision(start = 3)]
c: u64,
#[revision(start = 3, default_fn = "default_c")]
d: String,
}
impl TestStruct {
// 用于为新添加的字段设置默认值
fn default_c(_revision: u16) -> String {
"test_string".to_owned()
}
// 用于将字段从旧版本转换到最新版本
fn convert_b(&mut self, _revision: u16, value: u8) -> Result<(), Error> {
self.c = value as u64;
Ok(())
}
}
// 测试枚举当前版本为3
#[derive(Debug, PartialEq)]
#[revisioned(revision = 3)]
pub enum TestEnum {
#[revision(end = 2, convert_fn = "upgrade_zero")]
Zero,
#[revision(end = 2, convert_fn = "upgrade_one")]
One(u32),
#[revision(start = 2)]
Two(u64),
#[revision(start = 2)]
Three {
a: i64,
#[revision(end = 2, convert_fn = "upgrade_three_b")]
b: f32,
#[revision(start = 2)]
c: rust_decimal::Decimal,
#[revision(start = 3)]
d: String
},
}
impl TestEnum {
// 用于将旧的枚举变体转换为新的变体
fn upgrade_zero((): ()) -> Result<TestEnum, Error> {
Ok(Self::Two(0))
}
// 用于将旧的枚举变体转换为新的变体
fn upgrade_one((v0,): (u32,)) -> Result<TestEnum, Error> {
Ok(Self::Two(v0 as u64))
}
// 用于将字段从旧版本转换到最新版本
fn upgrade_three_b(&mut self, _revision: u16, value: f32) -> Result<(), Error> {
match self {
TestEnum::Three {
ref mut c,
..
} => {
*c = value.into();
}
_ => unreachable!(),
}
Ok(())
}
}
// 完整示例代码
use revision::{Revisioned, Error};
use std::io::{Cursor, Read, Write};
fn main() -> Result<(), Error> {
// 创建一个测试结构体实例
let mut test_struct = TestStruct {
a: 42,
b: 10,
c: 100,
d: "example".to_string(),
};
// 序列化到字节缓冲区
let mut buffer = Vec::new();
test_struct.serialize_revisioned(&mut buffer)?;
// 从字节缓冲区反序列化
let mut cursor = Cursor::new(buffer);
let deserialized: TestStruct = Revisioned::deserialize_revisioned(&mut cursor)?;
println!("Original: {:?}", test_struct);
println!("Deserialized: {:?}", deserialized);
// 测试枚举
let test_enum = TestEnum::Two(123);
let mut enum_buffer = Vec::new();
test_enum.serialize_revisioned(&mut enum_buffer)?;
let mut enum_cursor = Cursor::new(enum_buffer);
let deserialized_enum: TestEnum = Revisioned::deserialize_revisioned(&mut enum_cursor)?;
println!("Enum original: {:?}", test_enum);
println!("Enum deserialized: {:?}", deserialized_enum);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_struct_serialization() -> Result<(), Error> {
let original = TestStruct {
a: 100,
b: 5,
c: 50,
d: "test".to_string(),
};
let mut buffer = Vec::new();
original.serialize_revisioned(&mut buffer)?;
let mut cursor = Cursor::new(buffer);
let deserialized = TestStruct::deserialize_revisioned(&mut cursor)?;
assert_eq!(original, deserialized);
Ok(())
}
#[test]
fn test_enum_serialization() -> Result<(), Error> {
let original = TestEnum::Three {
a: -42,
b: 3.14,
c: rust_decimal::Decimal::new(12345, 2),
d: "enum_test".to_string(),
};
let mut buffer = Vec::new();
original.serialize_revisioned(&mut buffer)?;
let mut cursor = Cursor::new(buffer);
let deserialized = TestEnum::deserialize_revisioned(&mut cursor)?;
if let (TestEnum::Three { a: a1, c: c1, d: d1, .. }, TestEnum::Three { a: a2, c: c2, d: d2, .. }) = (&original, &deserialized) {
assert_eq!(a1, a2);
assert_eq!(c1, c2);
assert_eq!(d1, d2);
} else {
panic!("Enum variants don't match");
}
Ok(())
}
}
1 回复
Rust插件库revision的使用:高效版本控制与代码变更管理工具
简介
revision是一个专为Rust项目设计的轻量级版本控制和代码变更管理工具。它通过简单的宏和配置,帮助开发者跟踪代码变更、管理版本迁移,并支持多版本代码共存。
核心特性
- 轻量级版本标记系统
- 自动版本迁移管理
- 支持并行版本共存
- 零运行时开销
- 与Cargo工作流无缝集成
安装方法
在Cargo.toml中添加依赖:
[dependencies]
revision = "0.3"
基本使用方法
1. 版本标记
use revision::revisioned;
#[revisioned]
struct User {
#[rev(1)]
id: u64,
#[rev(1)]
name: String,
#[rev(2)] // 新增字段
email: Option<String>,
#[rev(3)] // 后续新增
age: Option<u32>,
}
2. 版本迁移实现
use revision::Revisioned;
impl Revisioned for User {
fn revision() -> u16 {
3 // 当前最新版本
}
fn migrate_to(version: u16) -> Option<Box<dyn Fn(&mut Self)>> {
match version {
1 => Some(Box::new(|user| {
// 从版本1迁移到最新
user.email = None;
user.age = None;
})),
2 => Some(Box::new(|user| {
// 从版本2迁移到最新
user.age = None;
})),
_ => None,
}
}
}
3. 数据迁移示例
let mut old_user = User {
id: 1,
name: "Alice".to_string(),
email: None,
age: None,
};
// 将旧版本数据迁移到最新版本
User::migrate(&mut old_user, 1); // 从版本1迁移
4. 版本检查与验证
// 检查数据版本
let current_version = User::revision();
// 验证数据是否需要迁移
if old_user.version() < current_version {
User::migrate(&mut old_user, old_user.version());
}
高级用法
多版本序列化
use serde::{Serialize, Deserialize};
use revision::revisioned;
#[revisioned]
#[derive(Serialize, Deserialize)]
struct Config {
#[rev(1)]
timeout: u32,
#[rev(2)]
retries: u8,
#[rev(3)]
enabled: bool,
}
// 根据不同版本进行序列化/反序列化
版本条件编译
#[revisioned]
enum Feature {
#[rev(1)]
Basic,
#[rev(2)]
Advanced,
#[rev(3)]
#[cfg(feature = "premium")]
Premium,
}
最佳实践
- 版本规划:在添加新字段时递增版本号
- 向后兼容:确保旧版本数据能够正确迁移
- 测试验证:为每个版本迁移编写测试用例
- 文档记录:记录每个版本的变更内容
示例:完整使用场景
use revision::{revisioned, Revisioned};
#[revisioned]
struct DatabaseConfig {
#[rev(1)]
host: String,
#[rev(1)]
port: u16,
#[rev(2)]
username: String,
#[rev(3)]
password: String,
#[rev(4)]
ssl: bool,
}
fn main() {
// 模拟旧版本配置
let mut old_config = DatabaseConfig {
host: "localhost".to_string(),
port: 5432,
username: "user".to_string(),
password: "".to_string(),
ssl: false,
};
// 设置版本标记(假设是版本2的数据)
old_config.set_version(2);
// 迁移到最新版本
DatabaseConfig::migrate(&mut old_config, old_config.version());
println!("迁移后的配置: {:?}", old_config);
}
完整示例demo
// 完整示例:用户数据版本迁移系统
use revision::{revisioned, Revisioned};
use serde::{Serialize, Deserialize};
// 定义版本化的用户结构体
#[revisioned]
#[derive(Debug, Serialize, Deserialize)]
struct User {
#[rev(1)] // 版本1引入
id: u64,
#[rev(1)] // 版本1引入
name: String,
#[rev(2)] // 版本2新增邮箱字段
email: Option<String>,
#[rev(3)] // 版本3新增年龄字段
age: Option<u32>,
#[rev(4)] // 版本4新增电话号码字段
phone: Option<String>,
}
// 实现Revisioned trait
impl Revisioned for User {
fn revision() -> u16 {
4 // 当前最新版本
}
fn migrate_to(version: u16) -> Option<Box<dyn Fn(&mut Self)>> {
match version {
1 => Some(Box::new(|user| {
// 从版本1迁移到最新:添加缺失字段的默认值
user.email = None;
user.age = None;
user.phone = None;
})),
2 => Some(Box::new(|user| {
// 从版本2迁移到最新:添加缺失字段的默认值
user.age = None;
user.phone = None;
})),
3 => Some(Box::new(|user| {
// 从版本3迁移到最新:添加缺失字段的默认值
user.phone = None;
})),
_ => None,
}
}
}
fn main() {
println!("=== 用户数据版本迁移演示 ===");
// 模拟版本1的用户数据(只有id和name)
let mut user_v1 = User {
id: 1,
name: "张三".to_string(),
email: None,
age: None,
phone: None,
};
user_v1.set_version(1); // 标记为版本1
println!("版本1用户数据: {:?}", user_v1);
println!("当前数据版本: {}", user_v1.version());
println!("最新结构体版本: {}", User::revision());
// 检查并执行迁移
if user_v1.version() < User::revision() {
println!("检测到需要版本迁移...");
User::migrate(&mut user_v1, user_v1.version());
println!("迁移完成后的用户数据: {:?}", user_v1);
println!("迁移后的数据版本: {}", user_v1.version());
}
// 模拟版本2的用户数据(有id、name、email)
let mut user_v2 = User {
id: 2,
name: "李四".to_string(),
email: Some("lisi@example.com".to_string()),
age: None,
phone: None,
};
user_v2.set_version(2); // 标记为版本2
println!("\n版本2用户数据迁移演示:");
println!("迁移前: {:?}", user_v2);
User::migrate(&mut user_v2, user_v2.version());
println!("迁移后: {:?}", user_v2);
// 序列化和反序列化演示
println!("\n=== 序列化演示 ===");
let serialized = serde_json::to_string(&user_v2).unwrap();
println!("序列化结果: {}", serialized);
let deserialized: User = serde_json::from_str(&serialized).unwrap();
println!("反序列化后版本: {}", deserialized.version());
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_version_1_migration() {
let mut user = User {
id: 1,
name: "测试用户".to_string(),
email: None,
age: None,
phone: None,
};
user.set_version(1);
// 执行迁移
User::migrate(&mut user, user.version());
assert_eq!(user.version(), 4);
assert!(user.email.is_none());
assert!(user.age.is_none());
assert!(user.phone.is_none());
}
#[test]
fn test_version_2_migration() {
let mut user = User {
id: 2,
name: "测试用户".to_string(),
email: Some("test@example.com".to_string()),
age: None,
phone: None,
};
user.set_version(2);
// 执行迁移
User::migrate(&mut user, user.version());
assert_eq!(user.version(), 4);
assert!(user.email.is_some());
assert!(user.age.is_none());
assert!(user.phone.is_none());
}
}
注意事项
- 版本号从1开始递增
- 确保所有迁移路径都经过测试
- 在生产环境中建议记录迁移日志
- 考虑使用特性标志来控制不同版本的代码路径
revision库为Rust项目提供了简单而强大的版本管理能力,特别适合需要长期维护和渐进式升级的项目。