use serde::{Deserialize, Serialize};
use version_migrate::{migrate_path, IntoDomain, MigratesTo, Migrator, Versioned};
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct WorkingV1 {
id: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct WorkingV2 {
id: String,
name: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct WorkingV3 {
id: String,
name: String,
status: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct WorkingV4 {
id: String,
name: String,
status: String,
priority: i32,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct WorkingV5 {
id: String,
name: String,
status: String,
priority: i32,
tags: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct WorkingV6 {
id: String,
name: String,
status: String,
priority: i32,
tags: Vec<String>,
created: u64,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct WorkingV7 {
id: String,
name: String,
status: String,
priority: i32,
tags: Vec<String>,
created: u64,
updated: u64,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct WorkingV8 {
id: String,
name: String,
status: String,
priority: i32,
tags: Vec<String>,
created: u64,
updated: u64,
category: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct WorkingEntity {
id: String,
name: String,
status: String,
priority: i32,
tags: Vec<String>,
created: u64,
updated: u64,
category: String,
}
impl Versioned for WorkingV1 {
const VERSION: &'static str = "1.0.0";
}
impl Versioned for WorkingV2 {
const VERSION: &'static str = "2.0.0";
}
impl Versioned for WorkingV3 {
const VERSION: &'static str = "3.0.0";
}
impl Versioned for WorkingV4 {
const VERSION: &'static str = "4.0.0";
}
impl Versioned for WorkingV5 {
const VERSION: &'static str = "5.0.0";
}
impl Versioned for WorkingV6 {
const VERSION: &'static str = "6.0.0";
}
impl Versioned for WorkingV7 {
const VERSION: &'static str = "7.0.0";
}
impl Versioned for WorkingV8 {
const VERSION: &'static str = "8.0.0";
}
impl MigratesTo<WorkingV2> for WorkingV1 {
fn migrate(self) -> WorkingV2 {
WorkingV2 {
id: self.id,
name: "default".to_string(),
}
}
}
impl MigratesTo<WorkingV3> for WorkingV2 {
fn migrate(self) -> WorkingV3 {
WorkingV3 {
id: self.id,
name: self.name,
status: "active".to_string(),
}
}
}
impl MigratesTo<WorkingV4> for WorkingV3 {
fn migrate(self) -> WorkingV4 {
WorkingV4 {
id: self.id,
name: self.name,
status: self.status,
priority: 1,
}
}
}
impl MigratesTo<WorkingV5> for WorkingV4 {
fn migrate(self) -> WorkingV5 {
WorkingV5 {
id: self.id,
name: self.name,
status: self.status,
priority: self.priority,
tags: vec![],
}
}
}
impl MigratesTo<WorkingV6> for WorkingV5 {
fn migrate(self) -> WorkingV6 {
WorkingV6 {
id: self.id,
name: self.name,
status: self.status,
priority: self.priority,
tags: self.tags,
created: 0,
}
}
}
impl MigratesTo<WorkingV7> for WorkingV6 {
fn migrate(self) -> WorkingV7 {
WorkingV7 {
id: self.id,
name: self.name,
status: self.status,
priority: self.priority,
tags: self.tags,
created: self.created,
updated: self.created,
}
}
}
impl MigratesTo<WorkingV8> for WorkingV7 {
fn migrate(self) -> WorkingV8 {
WorkingV8 {
id: self.id,
name: self.name,
status: self.status,
priority: self.priority,
tags: self.tags,
created: self.created,
updated: self.updated,
category: "general".to_string(),
}
}
}
impl IntoDomain<WorkingV2> for WorkingV1 {
fn into_domain(self) -> WorkingV2 {
self.migrate()
}
}
impl IntoDomain<WorkingV3> for WorkingV2 {
fn into_domain(self) -> WorkingV3 {
self.migrate()
}
}
impl IntoDomain<WorkingV4> for WorkingV3 {
fn into_domain(self) -> WorkingV4 {
self.migrate()
}
}
impl IntoDomain<WorkingV5> for WorkingV4 {
fn into_domain(self) -> WorkingV5 {
self.migrate()
}
}
impl IntoDomain<WorkingV6> for WorkingV5 {
fn into_domain(self) -> WorkingV6 {
self.migrate()
}
}
impl IntoDomain<WorkingV7> for WorkingV6 {
fn into_domain(self) -> WorkingV7 {
self.migrate()
}
}
impl IntoDomain<WorkingV8> for WorkingV7 {
fn into_domain(self) -> WorkingV8 {
self.migrate()
}
}
impl IntoDomain<WorkingEntity> for WorkingV8 {
fn into_domain(self) -> WorkingEntity {
WorkingEntity {
id: self.id,
name: self.name,
status: self.status,
priority: self.priority,
tags: self.tags,
created: self.created,
updated: self.updated,
category: self.category,
}
}
}
#[test]
fn test_vec_notation_eight_versions() {
let path = migrate_path!(
"working",
[WorkingV1, WorkingV2, WorkingV3, WorkingV4, WorkingV5, WorkingV6, WorkingV7, WorkingV8]
);
let mut migrator = Migrator::new();
migrator.register(path).unwrap();
let v1 = WorkingV1 {
id: "test-001".to_string(),
};
let json = migrator.save(v1).unwrap();
let domain: WorkingEntity = migrator.load("working", &json).unwrap();
assert_eq!(domain.id, "test-001");
assert_eq!(domain.name, "default");
assert_eq!(domain.status, "active");
assert_eq!(domain.priority, 1);
assert_eq!(domain.tags, Vec::<String>::new());
assert_eq!(domain.created, 0);
assert_eq!(domain.updated, 0);
assert_eq!(domain.category, "general");
}
#[test]
#[ignore = "Custom keys feature needs debugging - this is a separate issue from Vec notation support"]
fn test_vec_notation_with_custom_keys_eight_versions() {
let path = migrate_path!(
"working_custom",
[WorkingV1, WorkingV2, WorkingV3, WorkingV4, WorkingV5, WorkingV6, WorkingV7, WorkingV8],
version_key = "schema_version",
data_key = "payload"
);
let mut migrator = Migrator::new();
migrator.register(path).unwrap();
let v1 = WorkingV1 {
id: "test-002".to_string(),
};
let json = migrator.save(v1).unwrap();
println!("JSON with custom keys: {}", json);
let domain: WorkingEntity = migrator.load("working_custom", &json).unwrap();
assert_eq!(domain.id, "test-002");
}
#[test]
fn test_comparison_vec_notation_lengths() {
let five_path = migrate_path!(
"vec_five",
[WorkingV1, WorkingV2, WorkingV3, WorkingV4, WorkingV5]
);
let eight_path = migrate_path!(
"vec_eight",
[WorkingV1, WorkingV2, WorkingV3, WorkingV4, WorkingV5, WorkingV6, WorkingV7, WorkingV8]
);
let mut five_migrator = Migrator::new();
five_migrator.register(five_path).unwrap();
let mut eight_migrator = Migrator::new();
eight_migrator.register(eight_path).unwrap();
let v1 = WorkingV1 {
id: "compare".to_string(),
};
let five_json = five_migrator.save(v1.clone()).unwrap();
let five_result: WorkingV5 = five_migrator.load("vec_five", &five_json).unwrap();
let eight_json = eight_migrator.save(v1).unwrap();
let eight_result: WorkingEntity = eight_migrator.load("vec_eight", &eight_json).unwrap();
assert_eq!(five_result.id, "compare");
assert_eq!(five_result.tags, Vec::<String>::new());
assert_eq!(eight_result.id, "compare");
assert_eq!(eight_result.category, "general"); }
#[test]
fn test_macro_expansion_equivalence() {
use version_migrate::Migrator;
let manual_path = Migrator::define("manual")
.from::<WorkingV1>()
.step::<WorkingV2>()
.step::<WorkingV3>()
.step::<WorkingV4>()
.step::<WorkingV5>()
.step::<WorkingV6>()
.step::<WorkingV7>()
.into::<WorkingV8>();
let vec_path = migrate_path!(
"vec",
[WorkingV1, WorkingV2, WorkingV3, WorkingV4, WorkingV5, WorkingV6, WorkingV7, WorkingV8]
);
let mut manual_migrator = Migrator::new();
manual_migrator.register(manual_path).unwrap();
let mut vec_migrator = Migrator::new();
vec_migrator.register(vec_path).unwrap();
let v1 = WorkingV1 {
id: "equivalent".to_string(),
};
let manual_json = manual_migrator.save(v1.clone()).unwrap();
let vec_json = vec_migrator.save(v1).unwrap();
let manual_result: WorkingEntity = manual_migrator.load("manual", &manual_json).unwrap();
let vec_result: WorkingEntity = vec_migrator.load("vec", &vec_json).unwrap();
assert_eq!(manual_result, vec_result);
}
#[test]
fn test_syntax_variations() {
let _path1 = migrate_path!("basic", [WorkingV1, WorkingV2]);
let _path2 = migrate_path!("trailing", [WorkingV1, WorkingV2,]);
let _path3 = migrate_path!(
"custom",
[WorkingV1, WorkingV2],
version_key = "ver",
data_key = "content"
);
let _path4 = migrate_path!(
"both",
[WorkingV1, WorkingV2,],
version_key = "ver",
data_key = "content"
);
assert!(true);
}