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