1use crate::errors::MigrationError;
4use crate::{IntoDomain, MigratesTo, Versioned};
5use serde::de::DeserializeOwned;
6use serde::Serialize;
7use std::collections::HashMap;
8use std::marker::PhantomData;
9
10type MigrationFn =
11 Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync>;
12
13type DomainSaveFn =
15 Box<dyn Fn(serde_json::Value, &str, &str) -> Result<String, MigrationError> + Send + Sync>;
16type DomainSaveFlatFn =
17 Box<dyn Fn(serde_json::Value, &str) -> Result<String, MigrationError> + Send + Sync>;
18
19struct EntityMigrationPath {
21 steps: HashMap<String, MigrationFn>,
23 finalize:
25 Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync>,
26 versions: Vec<String>,
28 version_key: String,
30 data_key: String,
32}
33
34struct DomainSavers {
36 save_fn: DomainSaveFn,
37 save_flat_fn: DomainSaveFlatFn,
38}
39
40pub struct Migrator {
42 paths: HashMap<String, EntityMigrationPath>,
43 default_version_key: Option<String>,
44 default_data_key: Option<String>,
45 domain_savers: HashMap<String, DomainSavers>,
46}
47
48impl Migrator {
49 pub fn new() -> Self {
51 Self {
52 paths: HashMap::new(),
53 default_version_key: None,
54 default_data_key: None,
55 domain_savers: HashMap::new(),
56 }
57 }
58
59 pub fn get_latest_version(&self, entity: &str) -> Option<&str> {
65 self.paths
66 .get(entity)
67 .and_then(|path| path.versions.last())
68 .map(|v| v.as_str())
69 }
70
71 pub fn builder() -> MigratorBuilder {
82 MigratorBuilder::new()
83 }
84
85 pub fn define(entity: &str) -> MigrationPathBuilder<Start> {
87 MigrationPathBuilder::new(entity.to_string())
88 }
89
90 pub fn register<D>(&mut self, path: MigrationPath<D>) -> Result<(), MigrationError> {
100 Self::validate_migration_path(&path.entity, &path.versions)?;
101
102 let version_key = path
104 .custom_version_key
105 .or_else(|| self.default_version_key.clone())
106 .unwrap_or_else(|| path.inner.version_key.clone());
107
108 let data_key = path
109 .custom_data_key
110 .or_else(|| self.default_data_key.clone())
111 .unwrap_or_else(|| path.inner.data_key.clone());
112
113 let entity_name = path.entity.clone();
114 let final_path = EntityMigrationPath {
115 steps: path.inner.steps,
116 finalize: path.inner.finalize,
117 versions: path.versions,
118 version_key,
119 data_key,
120 };
121
122 self.paths.insert(path.entity, final_path);
123
124 if let (Some(save_fn), Some(save_flat_fn)) = (path.save_fn, path.save_flat_fn) {
126 self.domain_savers.insert(
127 entity_name,
128 DomainSavers {
129 save_fn,
130 save_flat_fn,
131 },
132 );
133 }
134
135 Ok(())
136 }
137
138 fn validate_migration_path(entity: &str, versions: &[String]) -> Result<(), MigrationError> {
140 Self::check_circular_path(entity, versions)?;
142
143 Self::check_version_ordering(entity, versions)?;
145
146 Ok(())
147 }
148
149 fn check_circular_path(entity: &str, versions: &[String]) -> Result<(), MigrationError> {
151 let mut seen = std::collections::HashSet::new();
152
153 for version in versions {
154 if !seen.insert(version) {
155 let path = versions.join(" -> ");
157 return Err(MigrationError::CircularMigrationPath {
158 entity: entity.to_string(),
159 path,
160 });
161 }
162 }
163
164 Ok(())
165 }
166
167 fn check_version_ordering(entity: &str, versions: &[String]) -> Result<(), MigrationError> {
169 for i in 0..versions.len().saturating_sub(1) {
170 let current = &versions[i];
171 let next = &versions[i + 1];
172
173 let current_ver = semver::Version::parse(current).map_err(|e| {
175 MigrationError::DeserializationError(format!("Invalid semver '{}': {}", current, e))
176 })?;
177
178 let next_ver = semver::Version::parse(next).map_err(|e| {
179 MigrationError::DeserializationError(format!("Invalid semver '{}': {}", next, e))
180 })?;
181
182 if next_ver <= current_ver {
184 return Err(MigrationError::InvalidVersionOrder {
185 entity: entity.to_string(),
186 from: current.clone(),
187 to: next.clone(),
188 });
189 }
190 }
191
192 Ok(())
193 }
194
195 pub fn load_from<D, T>(&self, entity: &str, data: T) -> Result<D, MigrationError>
228 where
229 D: DeserializeOwned,
230 T: Serialize,
231 {
232 let value = serde_json::to_value(data).map_err(|e| {
234 MigrationError::DeserializationError(format!(
235 "Failed to convert input data to internal format: {}",
236 e
237 ))
238 })?;
239
240 let path = self
242 .paths
243 .get(entity)
244 .ok_or_else(|| MigrationError::EntityNotFound(entity.to_string()))?;
245
246 let version_key = &path.version_key;
247 let data_key = &path.data_key;
248
249 let obj = value.as_object().ok_or_else(|| {
251 MigrationError::DeserializationError(
252 "Expected object with version and data fields".to_string(),
253 )
254 })?;
255
256 let current_version = obj
257 .get(version_key)
258 .and_then(|v| v.as_str())
259 .ok_or_else(|| {
260 MigrationError::DeserializationError(format!(
261 "Missing or invalid '{}' field",
262 version_key
263 ))
264 })?
265 .to_string();
266
267 let mut current_data = obj
268 .get(data_key)
269 .ok_or_else(|| {
270 MigrationError::DeserializationError(format!("Missing '{}' field", data_key))
271 })?
272 .clone();
273
274 let mut current_version = current_version;
275
276 while let Some(migrate_fn) = path.steps.get(¤t_version) {
278 current_data = migrate_fn(current_data.clone())?;
280
281 match path.versions.iter().position(|v| v == ¤t_version) {
284 Some(idx) if idx + 1 < path.versions.len() => {
285 current_version = path.versions[idx + 1].clone();
286 }
287 _ => break,
288 }
289 }
290
291 let domain_value = (path.finalize)(current_data)?;
293
294 serde_json::from_value(domain_value).map_err(|e| {
295 MigrationError::DeserializationError(format!("Failed to convert to domain: {}", e))
296 })
297 }
298
299 pub fn load<D: DeserializeOwned>(&self, entity: &str, json: &str) -> Result<D, MigrationError> {
327 let data: serde_json::Value = serde_json::from_str(json).map_err(|e| {
328 MigrationError::DeserializationError(format!("Failed to parse JSON: {}", e))
329 })?;
330 self.load_from(entity, data)
331 }
332
333 pub fn load_flat<D: DeserializeOwned>(
361 &self,
362 entity: &str,
363 json: &str,
364 ) -> Result<D, MigrationError> {
365 let data: serde_json::Value = serde_json::from_str(json).map_err(|e| {
366 MigrationError::DeserializationError(format!("Failed to parse JSON: {}", e))
367 })?;
368 self.load_flat_from(entity, data)
369 }
370
371 pub fn load_flat_from<D, T>(&self, entity: &str, value: T) -> Result<D, MigrationError>
400 where
401 D: DeserializeOwned,
402 T: Serialize,
403 {
404 let path = self
405 .paths
406 .get(entity)
407 .ok_or_else(|| MigrationError::EntityNotFound(entity.to_string()))?;
408
409 let version_key = &path.version_key;
410
411 let mut value = serde_json::to_value(value).map_err(|e| {
413 MigrationError::SerializationError(format!("Failed to convert input: {}", e))
414 })?;
415
416 let obj = value.as_object_mut().ok_or_else(|| {
418 MigrationError::DeserializationError(
419 "Expected object with version field at top level".to_string(),
420 )
421 })?;
422
423 let current_version = obj
424 .remove(version_key)
425 .ok_or_else(|| {
426 MigrationError::DeserializationError(format!(
427 "Missing '{}' field in flat format",
428 version_key
429 ))
430 })?
431 .as_str()
432 .ok_or_else(|| {
433 MigrationError::DeserializationError(format!(
434 "Invalid '{}' field type",
435 version_key
436 ))
437 })?
438 .to_string();
439
440 let mut current_data = serde_json::Value::Object(obj.clone());
442 let mut current_version = current_version;
443
444 while let Some(migrate_fn) = path.steps.get(¤t_version) {
446 current_data = migrate_fn(current_data.clone())?;
448
449 match path.versions.iter().position(|v| v == ¤t_version) {
451 Some(idx) if idx + 1 < path.versions.len() => {
452 current_version = path.versions[idx + 1].clone();
453 }
454 _ => break,
455 }
456 }
457
458 let domain_value = (path.finalize)(current_data)?;
460
461 serde_json::from_value(domain_value).map_err(|e| {
462 MigrationError::DeserializationError(format!("Failed to convert to domain: {}", e))
463 })
464 }
465
466 pub fn save<T: Versioned + Serialize>(&self, data: T) -> Result<String, MigrationError> {
497 let version_key = T::VERSION_KEY;
499 let data_key = T::DATA_KEY;
500
501 let data_value = serde_json::to_value(&data).map_err(|e| {
503 MigrationError::SerializationError(format!("Failed to serialize data: {}", e))
504 })?;
505
506 let mut map = serde_json::Map::new();
508 map.insert(
509 version_key.to_string(),
510 serde_json::Value::String(T::VERSION.to_string()),
511 );
512 map.insert(data_key.to_string(), data_value);
513
514 serde_json::to_string(&map).map_err(|e| {
515 MigrationError::SerializationError(format!("Failed to serialize wrapper: {}", e))
516 })
517 }
518
519 pub fn save_flat<T: Versioned + Serialize>(&self, data: T) -> Result<String, MigrationError> {
549 let version_key = T::VERSION_KEY;
550
551 let mut data_value = serde_json::to_value(&data).map_err(|e| {
553 MigrationError::SerializationError(format!("Failed to serialize data: {}", e))
554 })?;
555
556 let obj = data_value.as_object_mut().ok_or_else(|| {
558 MigrationError::SerializationError(
559 "Data must serialize to a JSON object for flat format".to_string(),
560 )
561 })?;
562
563 obj.insert(
565 version_key.to_string(),
566 serde_json::Value::String(T::VERSION.to_string()),
567 );
568
569 serde_json::to_string(&obj).map_err(|e| {
570 MigrationError::SerializationError(format!("Failed to serialize flat format: {}", e))
571 })
572 }
573
574 pub fn load_vec_from<D, T>(&self, entity: &str, data: Vec<T>) -> Result<Vec<D>, MigrationError>
607 where
608 D: DeserializeOwned,
609 T: Serialize,
610 {
611 data.into_iter()
612 .map(|item| self.load_from(entity, item))
613 .collect()
614 }
615
616 pub fn load_vec<D: DeserializeOwned>(
647 &self,
648 entity: &str,
649 json: &str,
650 ) -> Result<Vec<D>, MigrationError> {
651 let data: Vec<serde_json::Value> = serde_json::from_str(json).map_err(|e| {
652 MigrationError::DeserializationError(format!("Failed to parse JSON array: {}", e))
653 })?;
654 self.load_vec_from(entity, data)
655 }
656
657 pub fn load_vec_flat<D: DeserializeOwned>(
688 &self,
689 entity: &str,
690 json: &str,
691 ) -> Result<Vec<D>, MigrationError> {
692 let data: Vec<serde_json::Value> = serde_json::from_str(json).map_err(|e| {
693 MigrationError::DeserializationError(format!("Failed to parse JSON array: {}", e))
694 })?;
695 self.load_vec_flat_from(entity, data)
696 }
697
698 pub fn load_vec_flat_from<D, T>(
727 &self,
728 entity: &str,
729 data: Vec<T>,
730 ) -> Result<Vec<D>, MigrationError>
731 where
732 D: DeserializeOwned,
733 T: Serialize,
734 {
735 data.into_iter()
736 .map(|item| self.load_flat_from(entity, item))
737 .collect()
738 }
739
740 pub fn save_vec<T: Versioned + Serialize>(
777 &self,
778 data: Vec<T>,
779 ) -> Result<String, MigrationError> {
780 let version_key = T::VERSION_KEY;
781 let data_key = T::DATA_KEY;
782
783 let wrappers: Result<Vec<serde_json::Value>, MigrationError> = data
784 .into_iter()
785 .map(|item| {
786 let data_value = serde_json::to_value(&item).map_err(|e| {
787 MigrationError::SerializationError(format!("Failed to serialize item: {}", e))
788 })?;
789
790 let mut map = serde_json::Map::new();
791 map.insert(
792 version_key.to_string(),
793 serde_json::Value::String(T::VERSION.to_string()),
794 );
795 map.insert(data_key.to_string(), data_value);
796
797 Ok(serde_json::Value::Object(map))
798 })
799 .collect();
800
801 serde_json::to_string(&wrappers?).map_err(|e| {
802 MigrationError::SerializationError(format!("Failed to serialize data array: {}", e))
803 })
804 }
805
806 pub fn save_vec_flat<T: Versioned + Serialize>(
842 &self,
843 data: Vec<T>,
844 ) -> Result<String, MigrationError> {
845 let version_key = T::VERSION_KEY;
846
847 let flat_items: Result<Vec<serde_json::Value>, MigrationError> = data
848 .into_iter()
849 .map(|item| {
850 let mut data_value = serde_json::to_value(&item).map_err(|e| {
851 MigrationError::SerializationError(format!("Failed to serialize item: {}", e))
852 })?;
853
854 let obj = data_value.as_object_mut().ok_or_else(|| {
855 MigrationError::SerializationError(
856 "Data must serialize to a JSON object for flat format".to_string(),
857 )
858 })?;
859
860 obj.insert(
862 version_key.to_string(),
863 serde_json::Value::String(T::VERSION.to_string()),
864 );
865
866 Ok(serde_json::Value::Object(obj.clone()))
867 })
868 .collect();
869
870 serde_json::to_string(&flat_items?).map_err(|e| {
871 MigrationError::SerializationError(format!(
872 "Failed to serialize flat data array: {}",
873 e
874 ))
875 })
876 }
877
878 pub fn save_entity<E: crate::LatestVersioned>(
916 &self,
917 entity: E,
918 ) -> Result<String, MigrationError> {
919 let latest = entity.to_latest();
920 self.save(latest)
921 }
922
923 pub fn save_entity_flat<E: crate::LatestVersioned>(
961 &self,
962 entity: E,
963 ) -> Result<String, MigrationError> {
964 let latest = entity.to_latest();
965 self.save_flat(latest)
966 }
967
968 pub fn save_entity_vec<E: crate::LatestVersioned>(
996 &self,
997 entities: Vec<E>,
998 ) -> Result<String, MigrationError> {
999 let versioned: Vec<E::Latest> = entities.into_iter().map(|e| e.to_latest()).collect();
1000 self.save_vec(versioned)
1001 }
1002
1003 pub fn save_entity_vec_flat<E: crate::LatestVersioned>(
1031 &self,
1032 entities: Vec<E>,
1033 ) -> Result<String, MigrationError> {
1034 let versioned: Vec<E::Latest> = entities.into_iter().map(|e| e.to_latest()).collect();
1035 self.save_vec_flat(versioned)
1036 }
1037
1038 pub fn save_domain<T: Serialize>(
1076 &self,
1077 entity_name: &str,
1078 entity: T,
1079 ) -> Result<String, MigrationError> {
1080 let saver = self.domain_savers.get(entity_name).ok_or_else(|| {
1081 MigrationError::EntityNotFound(format!(
1082 "Entity '{}' is not registered with domain save support. Use into_with_save() when defining the migration path.",
1083 entity_name
1084 ))
1085 })?;
1086
1087 let path = self.paths.get(entity_name).ok_or_else(|| {
1089 MigrationError::EntityNotFound(format!("Entity '{}' is not registered", entity_name))
1090 })?;
1091
1092 let domain_value = serde_json::to_value(entity).map_err(|e| {
1093 MigrationError::SerializationError(format!("Failed to serialize entity: {}", e))
1094 })?;
1095
1096 (saver.save_fn)(domain_value, &path.version_key, &path.data_key)
1097 }
1098
1099 pub fn save_domain_flat<T: Serialize>(
1125 &self,
1126 entity_name: &str,
1127 entity: T,
1128 ) -> Result<String, MigrationError> {
1129 let saver = self.domain_savers.get(entity_name).ok_or_else(|| {
1130 MigrationError::EntityNotFound(format!(
1131 "Entity '{}' is not registered with domain save support. Use into_with_save() when defining the migration path.",
1132 entity_name
1133 ))
1134 })?;
1135
1136 let path = self.paths.get(entity_name).ok_or_else(|| {
1138 MigrationError::EntityNotFound(format!("Entity '{}' is not registered", entity_name))
1139 })?;
1140
1141 let domain_value = serde_json::to_value(entity).map_err(|e| {
1142 MigrationError::SerializationError(format!("Failed to serialize entity: {}", e))
1143 })?;
1144
1145 (saver.save_flat_fn)(domain_value, &path.version_key)
1146 }
1147}
1148
1149impl Default for Migrator {
1150 fn default() -> Self {
1151 Self::new()
1152 }
1153}
1154
1155pub struct MigratorBuilder {
1157 default_version_key: Option<String>,
1158 default_data_key: Option<String>,
1159}
1160
1161impl MigratorBuilder {
1162 pub(crate) fn new() -> Self {
1163 Self {
1164 default_version_key: None,
1165 default_data_key: None,
1166 }
1167 }
1168
1169 pub fn default_version_key(mut self, key: impl Into<String>) -> Self {
1175 self.default_version_key = Some(key.into());
1176 self
1177 }
1178
1179 pub fn default_data_key(mut self, key: impl Into<String>) -> Self {
1185 self.default_data_key = Some(key.into());
1186 self
1187 }
1188
1189 pub fn build(self) -> Migrator {
1191 Migrator {
1192 paths: HashMap::new(),
1193 default_version_key: self.default_version_key,
1194 default_data_key: self.default_data_key,
1195 domain_savers: HashMap::new(),
1196 }
1197 }
1198}
1199
1200pub struct Start;
1202
1203pub struct HasFrom<V>(PhantomData<V>);
1205
1206pub struct HasSteps<V>(PhantomData<V>);
1208
1209pub struct MigrationPathBuilder<State> {
1211 entity: String,
1212 steps: HashMap<String, MigrationFn>,
1213 versions: Vec<String>,
1214 version_key: String,
1215 data_key: String,
1216 custom_version_key: Option<String>,
1217 custom_data_key: Option<String>,
1218 _state: PhantomData<State>,
1219}
1220
1221impl MigrationPathBuilder<Start> {
1222 fn new(entity: String) -> Self {
1223 Self {
1224 entity,
1225 steps: HashMap::new(),
1226 versions: Vec::new(),
1227 version_key: String::from("version"),
1228 data_key: String::from("data"),
1229 custom_version_key: None,
1230 custom_data_key: None,
1231 _state: PhantomData,
1232 }
1233 }
1234
1235 pub fn with_keys(
1248 mut self,
1249 version_key: impl Into<String>,
1250 data_key: impl Into<String>,
1251 ) -> Self {
1252 self.custom_version_key = Some(version_key.into());
1253 self.custom_data_key = Some(data_key.into());
1254 self
1255 }
1256
1257 pub fn from<V: Versioned + DeserializeOwned>(self) -> MigrationPathBuilder<HasFrom<V>> {
1259 let mut versions = self.versions;
1260 versions.push(V::VERSION.to_string());
1261
1262 MigrationPathBuilder {
1263 entity: self.entity,
1264 steps: self.steps,
1265 versions,
1266 version_key: V::VERSION_KEY.to_string(),
1267 data_key: V::DATA_KEY.to_string(),
1268 custom_version_key: self.custom_version_key,
1269 custom_data_key: self.custom_data_key,
1270 _state: PhantomData,
1271 }
1272 }
1273}
1274
1275impl<V> MigrationPathBuilder<HasFrom<V>>
1276where
1277 V: Versioned + DeserializeOwned,
1278{
1279 pub fn step<Next>(mut self) -> MigrationPathBuilder<HasSteps<Next>>
1281 where
1282 V: MigratesTo<Next>,
1283 Next: Versioned + DeserializeOwned + Serialize,
1284 {
1285 let from_version = V::VERSION.to_string();
1286 let migration_fn: MigrationFn = Box::new(move |value| {
1287 let from_value: V = serde_json::from_value(value).map_err(|e| {
1288 MigrationError::DeserializationError(format!(
1289 "Failed to deserialize version {}: {}",
1290 V::VERSION,
1291 e
1292 ))
1293 })?;
1294
1295 let to_value = from_value.migrate();
1296
1297 serde_json::to_value(&to_value).map_err(|e| MigrationError::MigrationStepFailed {
1299 from: V::VERSION.to_string(),
1300 to: Next::VERSION.to_string(),
1301 error: e.to_string(),
1302 })
1303 });
1304
1305 self.steps.insert(from_version, migration_fn);
1306 self.versions.push(Next::VERSION.to_string());
1307
1308 MigrationPathBuilder {
1309 entity: self.entity,
1310 steps: self.steps,
1311 versions: self.versions,
1312 version_key: self.version_key,
1313 data_key: self.data_key,
1314 custom_version_key: self.custom_version_key,
1315 custom_data_key: self.custom_data_key,
1316 _state: PhantomData,
1317 }
1318 }
1319
1320 pub fn into<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1322 where
1323 V: IntoDomain<D>,
1324 {
1325 let finalize: Box<
1326 dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync,
1327 > = Box::new(move |value| {
1328 let versioned: V = serde_json::from_value(value).map_err(|e| {
1329 MigrationError::DeserializationError(format!(
1330 "Failed to deserialize final version: {}",
1331 e
1332 ))
1333 })?;
1334
1335 let domain = versioned.into_domain();
1336
1337 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1338 from: V::VERSION.to_string(),
1339 to: "domain".to_string(),
1340 error: e.to_string(),
1341 })
1342 });
1343
1344 MigrationPath {
1345 entity: self.entity,
1346 inner: EntityMigrationPath {
1347 steps: self.steps,
1348 finalize,
1349 versions: self.versions.clone(),
1350 version_key: self.version_key,
1351 data_key: self.data_key,
1352 },
1353 versions: self.versions,
1354 custom_version_key: self.custom_version_key,
1355 custom_data_key: self.custom_data_key,
1356 save_fn: None,
1357 save_flat_fn: None,
1358 _phantom: PhantomData,
1359 }
1360 }
1361
1362 pub fn into_with_save<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1397 where
1398 V: IntoDomain<D> + crate::FromDomain<D>,
1399 {
1400 let finalize: Box<
1401 dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync,
1402 > = Box::new(move |value| {
1403 let versioned: V = serde_json::from_value(value).map_err(|e| {
1404 MigrationError::DeserializationError(format!(
1405 "Failed to deserialize final version: {}",
1406 e
1407 ))
1408 })?;
1409
1410 let domain = versioned.into_domain();
1411
1412 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1413 from: V::VERSION.to_string(),
1414 to: "domain".to_string(),
1415 error: e.to_string(),
1416 })
1417 });
1418
1419 let version = V::VERSION;
1421
1422 let save_fn: DomainSaveFn = Box::new(move |domain_value, vkey, dkey| {
1423 let domain: D = serde_json::from_value(domain_value).map_err(|e| {
1424 MigrationError::DeserializationError(format!("Failed to deserialize domain: {}", e))
1425 })?;
1426
1427 let latest = V::from_domain(domain);
1428 let data_value = serde_json::to_value(&latest).map_err(|e| {
1429 MigrationError::SerializationError(format!("Failed to serialize latest: {}", e))
1430 })?;
1431
1432 let mut map = serde_json::Map::new();
1433 map.insert(
1434 vkey.to_string(),
1435 serde_json::Value::String(version.to_string()),
1436 );
1437 map.insert(dkey.to_string(), data_value);
1438
1439 serde_json::to_string(&map).map_err(|e| {
1440 MigrationError::SerializationError(format!("Failed to serialize wrapper: {}", e))
1441 })
1442 });
1443
1444 let save_flat_fn: DomainSaveFlatFn = Box::new(move |domain_value, vkey| {
1445 let domain: D = serde_json::from_value(domain_value).map_err(|e| {
1446 MigrationError::DeserializationError(format!("Failed to deserialize domain: {}", e))
1447 })?;
1448
1449 let latest = V::from_domain(domain);
1450 let mut data_value = serde_json::to_value(&latest).map_err(|e| {
1451 MigrationError::SerializationError(format!("Failed to serialize latest: {}", e))
1452 })?;
1453
1454 let obj = data_value.as_object_mut().ok_or_else(|| {
1455 MigrationError::SerializationError(
1456 "Data must serialize to a JSON object for flat format".to_string(),
1457 )
1458 })?;
1459
1460 obj.insert(
1461 vkey.to_string(),
1462 serde_json::Value::String(version.to_string()),
1463 );
1464
1465 serde_json::to_string(&obj).map_err(|e| {
1466 MigrationError::SerializationError(format!(
1467 "Failed to serialize flat format: {}",
1468 e
1469 ))
1470 })
1471 });
1472
1473 MigrationPath {
1474 entity: self.entity,
1475 inner: EntityMigrationPath {
1476 steps: self.steps,
1477 finalize,
1478 versions: self.versions.clone(),
1479 version_key: self.version_key,
1480 data_key: self.data_key,
1481 },
1482 versions: self.versions,
1483 custom_version_key: self.custom_version_key,
1484 custom_data_key: self.custom_data_key,
1485 save_fn: Some(save_fn),
1486 save_flat_fn: Some(save_flat_fn),
1487 _phantom: PhantomData,
1488 }
1489 }
1490}
1491
1492impl<V> MigrationPathBuilder<HasSteps<V>>
1493where
1494 V: Versioned + DeserializeOwned,
1495{
1496 pub fn step<Next>(mut self) -> MigrationPathBuilder<HasSteps<Next>>
1498 where
1499 V: MigratesTo<Next>,
1500 Next: Versioned + DeserializeOwned + Serialize,
1501 {
1502 let from_version = V::VERSION.to_string();
1503 let migration_fn: MigrationFn = Box::new(move |value| {
1504 let from_value: V = serde_json::from_value(value).map_err(|e| {
1505 MigrationError::DeserializationError(format!(
1506 "Failed to deserialize version {}: {}",
1507 V::VERSION,
1508 e
1509 ))
1510 })?;
1511
1512 let to_value = from_value.migrate();
1513
1514 serde_json::to_value(&to_value).map_err(|e| MigrationError::MigrationStepFailed {
1516 from: V::VERSION.to_string(),
1517 to: Next::VERSION.to_string(),
1518 error: e.to_string(),
1519 })
1520 });
1521
1522 self.steps.insert(from_version, migration_fn);
1523 self.versions.push(Next::VERSION.to_string());
1524
1525 MigrationPathBuilder {
1526 entity: self.entity,
1527 steps: self.steps,
1528 versions: self.versions,
1529 version_key: self.version_key,
1530 data_key: self.data_key,
1531 custom_version_key: self.custom_version_key,
1532 custom_data_key: self.custom_data_key,
1533 _state: PhantomData,
1534 }
1535 }
1536
1537 pub fn into<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1539 where
1540 V: IntoDomain<D>,
1541 {
1542 let finalize: Box<
1543 dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync,
1544 > = Box::new(move |value| {
1545 let versioned: V = serde_json::from_value(value).map_err(|e| {
1546 MigrationError::DeserializationError(format!(
1547 "Failed to deserialize final version: {}",
1548 e
1549 ))
1550 })?;
1551
1552 let domain = versioned.into_domain();
1553
1554 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1555 from: V::VERSION.to_string(),
1556 to: "domain".to_string(),
1557 error: e.to_string(),
1558 })
1559 });
1560
1561 MigrationPath {
1562 entity: self.entity,
1563 inner: EntityMigrationPath {
1564 steps: self.steps,
1565 finalize,
1566 versions: self.versions.clone(),
1567 version_key: self.version_key,
1568 data_key: self.data_key,
1569 },
1570 versions: self.versions,
1571 custom_version_key: self.custom_version_key,
1572 custom_data_key: self.custom_data_key,
1573 save_fn: None,
1574 save_flat_fn: None,
1575 _phantom: PhantomData,
1576 }
1577 }
1578
1579 pub fn into_with_save<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1583 where
1584 V: IntoDomain<D> + crate::FromDomain<D>,
1585 {
1586 let finalize: Box<
1587 dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync,
1588 > = Box::new(move |value| {
1589 let versioned: V = serde_json::from_value(value).map_err(|e| {
1590 MigrationError::DeserializationError(format!(
1591 "Failed to deserialize final version: {}",
1592 e
1593 ))
1594 })?;
1595
1596 let domain = versioned.into_domain();
1597
1598 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1599 from: V::VERSION.to_string(),
1600 to: "domain".to_string(),
1601 error: e.to_string(),
1602 })
1603 });
1604
1605 let version = V::VERSION;
1607
1608 let save_fn: DomainSaveFn = Box::new(move |domain_value, vkey, dkey| {
1609 let domain: D = serde_json::from_value(domain_value).map_err(|e| {
1610 MigrationError::DeserializationError(format!("Failed to deserialize domain: {}", e))
1611 })?;
1612
1613 let latest = V::from_domain(domain);
1614 let data_value = serde_json::to_value(&latest).map_err(|e| {
1615 MigrationError::SerializationError(format!("Failed to serialize latest: {}", e))
1616 })?;
1617
1618 let mut map = serde_json::Map::new();
1619 map.insert(
1620 vkey.to_string(),
1621 serde_json::Value::String(version.to_string()),
1622 );
1623 map.insert(dkey.to_string(), data_value);
1624
1625 serde_json::to_string(&map).map_err(|e| {
1626 MigrationError::SerializationError(format!("Failed to serialize wrapper: {}", e))
1627 })
1628 });
1629
1630 let save_flat_fn: DomainSaveFlatFn = Box::new(move |domain_value, vkey| {
1631 let domain: D = serde_json::from_value(domain_value).map_err(|e| {
1632 MigrationError::DeserializationError(format!("Failed to deserialize domain: {}", e))
1633 })?;
1634
1635 let latest = V::from_domain(domain);
1636 let mut data_value = serde_json::to_value(&latest).map_err(|e| {
1637 MigrationError::SerializationError(format!("Failed to serialize latest: {}", e))
1638 })?;
1639
1640 let obj = data_value.as_object_mut().ok_or_else(|| {
1641 MigrationError::SerializationError(
1642 "Data must serialize to a JSON object for flat format".to_string(),
1643 )
1644 })?;
1645
1646 obj.insert(
1647 vkey.to_string(),
1648 serde_json::Value::String(version.to_string()),
1649 );
1650
1651 serde_json::to_string(&obj).map_err(|e| {
1652 MigrationError::SerializationError(format!(
1653 "Failed to serialize flat format: {}",
1654 e
1655 ))
1656 })
1657 });
1658
1659 MigrationPath {
1660 entity: self.entity,
1661 inner: EntityMigrationPath {
1662 steps: self.steps,
1663 finalize,
1664 versions: self.versions.clone(),
1665 version_key: self.version_key,
1666 data_key: self.data_key,
1667 },
1668 versions: self.versions,
1669 custom_version_key: self.custom_version_key,
1670 custom_data_key: self.custom_data_key,
1671 save_fn: Some(save_fn),
1672 save_flat_fn: Some(save_flat_fn),
1673 _phantom: PhantomData,
1674 }
1675 }
1676}
1677
1678pub struct MigrationPath<D> {
1680 entity: String,
1681 inner: EntityMigrationPath,
1682 versions: Vec<String>,
1684 custom_version_key: Option<String>,
1686 custom_data_key: Option<String>,
1688 save_fn: Option<DomainSaveFn>,
1690 save_flat_fn: Option<DomainSaveFlatFn>,
1692 _phantom: PhantomData<D>,
1693}
1694
1695pub struct ConfigMigrator {
1730 root: serde_json::Value,
1731 migrator: Migrator,
1732}
1733
1734impl ConfigMigrator {
1735 pub fn from(json: &str, migrator: Migrator) -> Result<Self, MigrationError> {
1741 let root = serde_json::from_str(json)
1742 .map_err(|e| MigrationError::DeserializationError(e.to_string()))?;
1743 Ok(Self { root, migrator })
1744 }
1745
1746 pub fn query<T>(&self, key: &str) -> Result<Vec<T>, MigrationError>
1766 where
1767 T: crate::Queryable + for<'de> serde::Deserialize<'de>,
1768 {
1769 let value = &self.root[key];
1770 if value.is_null() {
1771 return Ok(Vec::new());
1772 }
1773
1774 if !value.is_array() {
1775 return Err(MigrationError::DeserializationError(format!(
1776 "Key '{}' does not contain an array",
1777 key
1778 )));
1779 }
1780
1781 let array = value.as_array().unwrap(); self.migrator
1783 .load_vec_flat_from(T::ENTITY_NAME, array.to_vec())
1784 }
1785
1786 pub fn update<T>(&mut self, key: &str, data: Vec<T>) -> Result<(), MigrationError>
1807 where
1808 T: serde::Serialize + crate::Queryable,
1809 {
1810 let entity_name = T::ENTITY_NAME;
1811 let latest_version = self
1812 .migrator
1813 .get_latest_version(entity_name)
1814 .ok_or_else(|| MigrationError::EntityNotFound(entity_name.to_string()))?;
1815
1816 let items: Vec<serde_json::Value> = data
1818 .into_iter()
1819 .map(|item| {
1820 let mut obj = serde_json::to_value(&item)
1821 .map_err(|e| MigrationError::SerializationError(e.to_string()))?;
1822
1823 if let Some(obj_map) = obj.as_object_mut() {
1824 obj_map.insert(
1825 "version".to_string(),
1826 serde_json::Value::String(latest_version.to_string()),
1827 );
1828 }
1829
1830 Ok(obj)
1831 })
1832 .collect::<Result<Vec<_>, MigrationError>>()?;
1833
1834 self.root[key] = serde_json::Value::Array(items);
1835 Ok(())
1836 }
1837
1838 pub fn to_string(&self) -> Result<String, MigrationError> {
1844 serde_json::to_string_pretty(&self.root)
1845 .map_err(|e| MigrationError::SerializationError(e.to_string()))
1846 }
1847
1848 pub fn to_string_compact(&self) -> Result<String, MigrationError> {
1854 serde_json::to_string(&self.root)
1855 .map_err(|e| MigrationError::SerializationError(e.to_string()))
1856 }
1857
1858 pub fn as_value(&self) -> &serde_json::Value {
1860 &self.root
1861 }
1862}
1863
1864#[cfg(test)]
1865mod tests {
1866 use super::*;
1867 use crate::{IntoDomain, MigratesTo, Versioned, VersionedWrapper};
1868 use serde::{Deserialize, Serialize};
1869
1870 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1872 struct V1 {
1873 value: String,
1874 }
1875
1876 impl Versioned for V1 {
1877 const VERSION: &'static str = "1.0.0";
1878 }
1879
1880 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1881 struct V2 {
1882 value: String,
1883 count: u32,
1884 }
1885
1886 impl Versioned for V2 {
1887 const VERSION: &'static str = "2.0.0";
1888 }
1889
1890 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1891 struct V3 {
1892 value: String,
1893 count: u32,
1894 enabled: bool,
1895 }
1896
1897 impl Versioned for V3 {
1898 const VERSION: &'static str = "3.0.0";
1899 }
1900
1901 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1902 struct Domain {
1903 value: String,
1904 count: u32,
1905 enabled: bool,
1906 }
1907
1908 impl MigratesTo<V2> for V1 {
1909 fn migrate(self) -> V2 {
1910 V2 {
1911 value: self.value,
1912 count: 0,
1913 }
1914 }
1915 }
1916
1917 impl MigratesTo<V3> for V2 {
1918 fn migrate(self) -> V3 {
1919 V3 {
1920 value: self.value,
1921 count: self.count,
1922 enabled: true,
1923 }
1924 }
1925 }
1926
1927 impl IntoDomain<Domain> for V3 {
1928 fn into_domain(self) -> Domain {
1929 Domain {
1930 value: self.value,
1931 count: self.count,
1932 enabled: self.enabled,
1933 }
1934 }
1935 }
1936
1937 #[test]
1938 fn test_migrator_new() {
1939 let migrator = Migrator::new();
1940 assert_eq!(migrator.paths.len(), 0);
1941 }
1942
1943 #[test]
1944 fn test_migrator_default() {
1945 let migrator = Migrator::default();
1946 assert_eq!(migrator.paths.len(), 0);
1947 }
1948
1949 #[test]
1950 fn test_single_step_migration() {
1951 let path = Migrator::define("test")
1952 .from::<V2>()
1953 .step::<V3>()
1954 .into::<Domain>();
1955
1956 let mut migrator = Migrator::new();
1957 migrator.register(path).unwrap();
1958
1959 let v2 = V2 {
1960 value: "test".to_string(),
1961 count: 42,
1962 };
1963 let wrapper = VersionedWrapper::from_versioned(v2);
1964 let json = serde_json::to_string(&wrapper).unwrap();
1965
1966 let result: Domain = migrator.load("test", &json).unwrap();
1967 assert_eq!(result.value, "test");
1968 assert_eq!(result.count, 42);
1969 assert!(result.enabled);
1970 }
1971
1972 #[test]
1973 fn test_multi_step_migration() {
1974 let path = Migrator::define("test")
1975 .from::<V1>()
1976 .step::<V2>()
1977 .step::<V3>()
1978 .into::<Domain>();
1979
1980 let mut migrator = Migrator::new();
1981 migrator.register(path).unwrap();
1982
1983 let v1 = V1 {
1984 value: "multi_step".to_string(),
1985 };
1986 let wrapper = VersionedWrapper::from_versioned(v1);
1987 let json = serde_json::to_string(&wrapper).unwrap();
1988
1989 let result: Domain = migrator.load("test", &json).unwrap();
1990 assert_eq!(result.value, "multi_step");
1991 assert_eq!(result.count, 0);
1992 assert!(result.enabled);
1993 }
1994
1995 #[test]
1996 fn test_no_migration_needed() {
1997 let path = Migrator::define("test").from::<V3>().into::<Domain>();
1998
1999 let mut migrator = Migrator::new();
2000 migrator.register(path).unwrap();
2001
2002 let v3 = V3 {
2003 value: "latest".to_string(),
2004 count: 100,
2005 enabled: false,
2006 };
2007 let wrapper = VersionedWrapper::from_versioned(v3);
2008 let json = serde_json::to_string(&wrapper).unwrap();
2009
2010 let result: Domain = migrator.load("test", &json).unwrap();
2011 assert_eq!(result.value, "latest");
2012 assert_eq!(result.count, 100);
2013 assert!(!result.enabled);
2014 }
2015
2016 #[test]
2017 fn test_entity_not_found() {
2018 let migrator = Migrator::new();
2019
2020 let v1 = V1 {
2021 value: "test".to_string(),
2022 };
2023 let wrapper = VersionedWrapper::from_versioned(v1);
2024 let json = serde_json::to_string(&wrapper).unwrap();
2025
2026 let result: Result<Domain, MigrationError> = migrator.load("unknown", &json);
2027 assert!(matches!(result, Err(MigrationError::EntityNotFound(_))));
2028
2029 if let Err(MigrationError::EntityNotFound(entity)) = result {
2030 assert_eq!(entity, "unknown");
2031 }
2032 }
2033
2034 #[test]
2035 fn test_invalid_json() {
2036 let path = Migrator::define("test").from::<V3>().into::<Domain>();
2037
2038 let mut migrator = Migrator::new();
2039 migrator.register(path).unwrap();
2040
2041 let invalid_json = "{ invalid json }";
2042 let result: Result<Domain, MigrationError> = migrator.load("test", invalid_json);
2043
2044 assert!(matches!(
2045 result,
2046 Err(MigrationError::DeserializationError(_))
2047 ));
2048 }
2049
2050 #[test]
2051 fn test_multiple_entities() {
2052 #[derive(Serialize, Deserialize, Debug, PartialEq)]
2053 struct OtherDomain {
2054 value: String,
2055 }
2056
2057 impl IntoDomain<OtherDomain> for V1 {
2058 fn into_domain(self) -> OtherDomain {
2059 OtherDomain { value: self.value }
2060 }
2061 }
2062
2063 let path1 = Migrator::define("entity1")
2064 .from::<V1>()
2065 .step::<V2>()
2066 .step::<V3>()
2067 .into::<Domain>();
2068
2069 let path2 = Migrator::define("entity2")
2070 .from::<V1>()
2071 .into::<OtherDomain>();
2072
2073 let mut migrator = Migrator::new();
2074 migrator.register(path1).unwrap();
2075 migrator.register(path2).unwrap();
2076
2077 let v1 = V1 {
2079 value: "entity1".to_string(),
2080 };
2081 let wrapper = VersionedWrapper::from_versioned(v1);
2082 let json = serde_json::to_string(&wrapper).unwrap();
2083 let result: Domain = migrator.load("entity1", &json).unwrap();
2084 assert_eq!(result.value, "entity1");
2085
2086 let v1 = V1 {
2088 value: "entity2".to_string(),
2089 };
2090 let wrapper = VersionedWrapper::from_versioned(v1);
2091 let json = serde_json::to_string(&wrapper).unwrap();
2092 let result: OtherDomain = migrator.load("entity2", &json).unwrap();
2093 assert_eq!(result.value, "entity2");
2094 }
2095
2096 #[test]
2097 fn test_save() {
2098 let migrator = Migrator::new();
2099
2100 let v1 = V1 {
2101 value: "test_save".to_string(),
2102 };
2103
2104 let json = migrator.save(v1).unwrap();
2105
2106 assert!(json.contains("\"version\""));
2108 assert!(json.contains("\"1.0.0\""));
2109 assert!(json.contains("\"data\""));
2110 assert!(json.contains("\"test_save\""));
2111
2112 let parsed: VersionedWrapper<serde_json::Value> = serde_json::from_str(&json).unwrap();
2114 assert_eq!(parsed.version, "1.0.0");
2115 }
2116
2117 #[test]
2118 fn test_save_and_load_roundtrip() {
2119 let path = Migrator::define("test")
2120 .from::<V1>()
2121 .step::<V2>()
2122 .step::<V3>()
2123 .into::<Domain>();
2124
2125 let mut migrator = Migrator::new();
2126 migrator.register(path).unwrap();
2127
2128 let v1 = V1 {
2130 value: "roundtrip".to_string(),
2131 };
2132 let json = migrator.save(v1).unwrap();
2133
2134 let domain: Domain = migrator.load("test", &json).unwrap();
2136
2137 assert_eq!(domain.value, "roundtrip");
2138 assert_eq!(domain.count, 0); assert!(domain.enabled); }
2141
2142 #[test]
2143 fn test_save_latest_version() {
2144 let migrator = Migrator::new();
2145
2146 let v3 = V3 {
2147 value: "latest".to_string(),
2148 count: 42,
2149 enabled: false,
2150 };
2151
2152 let json = migrator.save(v3).unwrap();
2153
2154 assert!(json.contains("\"version\":\"3.0.0\""));
2156 assert!(json.contains("\"value\":\"latest\""));
2157 assert!(json.contains("\"count\":42"));
2158 assert!(json.contains("\"enabled\":false"));
2159 }
2160
2161 #[test]
2162 fn test_save_pretty() {
2163 let migrator = Migrator::new();
2164
2165 let v2 = V2 {
2166 value: "pretty".to_string(),
2167 count: 10,
2168 };
2169
2170 let json = migrator.save(v2).unwrap();
2171
2172 assert!(!json.contains('\n'));
2174 assert!(json.contains("\"version\":\"2.0.0\""));
2175 }
2176
2177 #[test]
2178 fn test_validation_invalid_version_order() {
2179 let entity = "test".to_string();
2181 let versions = vec!["2.0.0".to_string(), "1.0.0".to_string()]; let result = Migrator::validate_migration_path(&entity, &versions);
2184 assert!(matches!(
2185 result,
2186 Err(MigrationError::InvalidVersionOrder { .. })
2187 ));
2188
2189 if let Err(MigrationError::InvalidVersionOrder {
2190 entity: e,
2191 from,
2192 to,
2193 }) = result
2194 {
2195 assert_eq!(e, "test");
2196 assert_eq!(from, "2.0.0");
2197 assert_eq!(to, "1.0.0");
2198 }
2199 }
2200
2201 #[test]
2202 fn test_validation_circular_path() {
2203 let entity = "test".to_string();
2205 let versions = vec![
2206 "1.0.0".to_string(),
2207 "2.0.0".to_string(),
2208 "1.0.0".to_string(), ];
2210
2211 let result = Migrator::validate_migration_path(&entity, &versions);
2212 assert!(matches!(
2213 result,
2214 Err(MigrationError::CircularMigrationPath { .. })
2215 ));
2216
2217 if let Err(MigrationError::CircularMigrationPath { entity: e, path }) = result {
2218 assert_eq!(e, "test");
2219 assert!(path.contains("1.0.0"));
2220 assert!(path.contains("2.0.0"));
2221 }
2222 }
2223
2224 #[test]
2225 fn test_validation_valid_path() {
2226 let entity = "test".to_string();
2228 let versions = vec![
2229 "1.0.0".to_string(),
2230 "1.1.0".to_string(),
2231 "2.0.0".to_string(),
2232 ];
2233
2234 let result = Migrator::validate_migration_path(&entity, &versions);
2235 assert!(result.is_ok());
2236 }
2237
2238 #[test]
2239 fn test_validation_empty_path() {
2240 let entity = "test".to_string();
2242 let versions = vec![];
2243
2244 let result = Migrator::validate_migration_path(&entity, &versions);
2245 assert!(result.is_ok());
2246 }
2247
2248 #[test]
2249 fn test_validation_single_version() {
2250 let entity = "test".to_string();
2252 let versions = vec!["1.0.0".to_string()];
2253
2254 let result = Migrator::validate_migration_path(&entity, &versions);
2255 assert!(result.is_ok());
2256 }
2257
2258 #[test]
2260 fn test_save_vec_and_load_vec() {
2261 let migrator = Migrator::new();
2262
2263 let items = vec![
2265 V1 {
2266 value: "item1".to_string(),
2267 },
2268 V1 {
2269 value: "item2".to_string(),
2270 },
2271 V1 {
2272 value: "item3".to_string(),
2273 },
2274 ];
2275
2276 let json = migrator.save_vec(items).unwrap();
2277
2278 assert!(json.starts_with('['));
2280 assert!(json.ends_with(']'));
2281 assert!(json.contains("\"version\":\"1.0.0\""));
2282 assert!(json.contains("item1"));
2283 assert!(json.contains("item2"));
2284 assert!(json.contains("item3"));
2285
2286 let path = Migrator::define("test")
2288 .from::<V1>()
2289 .step::<V2>()
2290 .step::<V3>()
2291 .into::<Domain>();
2292
2293 let mut migrator = Migrator::new();
2294 migrator.register(path).unwrap();
2295
2296 let domains: Vec<Domain> = migrator.load_vec("test", &json).unwrap();
2298
2299 assert_eq!(domains.len(), 3);
2300 assert_eq!(domains[0].value, "item1");
2301 assert_eq!(domains[1].value, "item2");
2302 assert_eq!(domains[2].value, "item3");
2303
2304 for domain in &domains {
2306 assert_eq!(domain.count, 0);
2307 assert!(domain.enabled);
2308 }
2309 }
2310
2311 #[test]
2312 fn test_load_vec_empty_array() {
2313 let path = Migrator::define("test")
2314 .from::<V1>()
2315 .step::<V2>()
2316 .step::<V3>()
2317 .into::<Domain>();
2318
2319 let mut migrator = Migrator::new();
2320 migrator.register(path).unwrap();
2321
2322 let json = "[]";
2323 let domains: Vec<Domain> = migrator.load_vec("test", json).unwrap();
2324
2325 assert_eq!(domains.len(), 0);
2326 }
2327
2328 #[test]
2329 fn test_load_vec_mixed_versions() {
2330 let path = Migrator::define("test")
2332 .from::<V1>()
2333 .step::<V2>()
2334 .step::<V3>()
2335 .into::<Domain>();
2336
2337 let mut migrator = Migrator::new();
2338 migrator.register(path).unwrap();
2339
2340 let json = r#"[
2342 {"version":"1.0.0","data":{"value":"v1-item"}},
2343 {"version":"2.0.0","data":{"value":"v2-item","count":42}},
2344 {"version":"3.0.0","data":{"value":"v3-item","count":99,"enabled":false}}
2345 ]"#;
2346
2347 let domains: Vec<Domain> = migrator.load_vec("test", json).unwrap();
2348
2349 assert_eq!(domains.len(), 3);
2350
2351 assert_eq!(domains[0].value, "v1-item");
2353 assert_eq!(domains[0].count, 0);
2354 assert!(domains[0].enabled);
2355
2356 assert_eq!(domains[1].value, "v2-item");
2358 assert_eq!(domains[1].count, 42);
2359 assert!(domains[1].enabled);
2360
2361 assert_eq!(domains[2].value, "v3-item");
2363 assert_eq!(domains[2].count, 99);
2364 assert!(!domains[2].enabled);
2365 }
2366
2367 #[test]
2368 fn test_load_vec_from_json_values() {
2369 let path = Migrator::define("test")
2370 .from::<V1>()
2371 .step::<V2>()
2372 .step::<V3>()
2373 .into::<Domain>();
2374
2375 let mut migrator = Migrator::new();
2376 migrator.register(path).unwrap();
2377
2378 let values: Vec<serde_json::Value> = vec![
2380 serde_json::json!({"version":"1.0.0","data":{"value":"direct1"}}),
2381 serde_json::json!({"version":"1.0.0","data":{"value":"direct2"}}),
2382 ];
2383
2384 let domains: Vec<Domain> = migrator.load_vec_from("test", values).unwrap();
2385
2386 assert_eq!(domains.len(), 2);
2387 assert_eq!(domains[0].value, "direct1");
2388 assert_eq!(domains[1].value, "direct2");
2389 }
2390
2391 #[test]
2392 fn test_save_vec_empty() {
2393 let migrator = Migrator::new();
2394 let empty: Vec<V1> = vec![];
2395
2396 let json = migrator.save_vec(empty).unwrap();
2397
2398 assert_eq!(json, "[]");
2399 }
2400
2401 #[test]
2402 fn test_load_vec_invalid_json() {
2403 let path = Migrator::define("test")
2404 .from::<V1>()
2405 .step::<V2>()
2406 .step::<V3>()
2407 .into::<Domain>();
2408
2409 let mut migrator = Migrator::new();
2410 migrator.register(path).unwrap();
2411
2412 let invalid_json = "{ not an array }";
2413 let result: Result<Vec<Domain>, MigrationError> = migrator.load_vec("test", invalid_json);
2414
2415 assert!(matches!(
2416 result,
2417 Err(MigrationError::DeserializationError(_))
2418 ));
2419 }
2420
2421 #[test]
2422 fn test_load_vec_entity_not_found() {
2423 let migrator = Migrator::new();
2424
2425 let json = r#"[{"version":"1.0.0","data":{"value":"test"}}]"#;
2426 let result: Result<Vec<Domain>, MigrationError> = migrator.load_vec("unknown", json);
2427
2428 assert!(matches!(result, Err(MigrationError::EntityNotFound(_))));
2429 }
2430
2431 #[test]
2432 fn test_save_vec_latest_version() {
2433 let migrator = Migrator::new();
2434
2435 let items = vec![
2436 V3 {
2437 value: "latest1".to_string(),
2438 count: 10,
2439 enabled: true,
2440 },
2441 V3 {
2442 value: "latest2".to_string(),
2443 count: 20,
2444 enabled: false,
2445 },
2446 ];
2447
2448 let json = migrator.save_vec(items).unwrap();
2449
2450 assert!(json.contains("\"version\":\"3.0.0\""));
2452 assert!(json.contains("latest1"));
2453 assert!(json.contains("latest2"));
2454 assert!(json.contains("\"count\":10"));
2455 assert!(json.contains("\"count\":20"));
2456 }
2457}