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