1use crate::errors::MigrationError;
4use crate::forward::{ForwardContext, Forwardable};
5use crate::{IntoDomain, MigratesTo, Versioned};
6use serde::de::DeserializeOwned;
7use serde::Serialize;
8use std::collections::HashMap;
9use std::marker::PhantomData;
10
11type MigrationFn =
12 Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync>;
13
14type DomainSaveFn =
16 Box<dyn Fn(serde_json::Value, &str, &str) -> Result<String, MigrationError> + Send + Sync>;
17type DomainSaveFlatFn =
18 Box<dyn Fn(serde_json::Value, &str) -> Result<String, MigrationError> + Send + Sync>;
19
20struct EntityMigrationPath {
22 steps: HashMap<String, MigrationFn>,
24 finalize:
26 Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync>,
27 versions: Vec<String>,
29 version_key: String,
31 data_key: String,
33}
34
35struct DomainSavers {
37 save_fn: DomainSaveFn,
38 save_flat_fn: DomainSaveFlatFn,
39}
40
41pub struct Migrator {
43 paths: HashMap<String, EntityMigrationPath>,
44 default_version_key: Option<String>,
45 default_data_key: Option<String>,
46 domain_savers: HashMap<String, DomainSavers>,
47}
48
49impl Migrator {
50 pub fn new() -> Self {
52 Self {
53 paths: HashMap::new(),
54 default_version_key: None,
55 default_data_key: None,
56 domain_savers: HashMap::new(),
57 }
58 }
59
60 pub fn get_latest_version(&self, entity: &str) -> Option<&str> {
66 self.paths
67 .get(entity)
68 .and_then(|path| path.versions.last())
69 .map(|v| v.as_str())
70 }
71
72 pub fn builder() -> MigratorBuilder {
83 MigratorBuilder::new()
84 }
85
86 pub fn define(entity: &str) -> MigrationPathBuilder<Start> {
88 MigrationPathBuilder::new(entity.to_string())
89 }
90
91 pub fn register<D>(&mut self, path: MigrationPath<D>) -> Result<(), MigrationError> {
101 Self::validate_migration_path(&path.entity, &path.versions)?;
102
103 let version_key = path
105 .custom_version_key
106 .or_else(|| self.default_version_key.clone())
107 .unwrap_or_else(|| path.inner.version_key.clone());
108
109 let data_key = path
110 .custom_data_key
111 .or_else(|| self.default_data_key.clone())
112 .unwrap_or_else(|| path.inner.data_key.clone());
113
114 let entity_name = path.entity.clone();
115 let final_path = EntityMigrationPath {
116 steps: path.inner.steps,
117 finalize: path.inner.finalize,
118 versions: path.versions,
119 version_key,
120 data_key,
121 };
122
123 self.paths.insert(path.entity, final_path);
124
125 if let (Some(save_fn), Some(save_flat_fn)) = (path.save_fn, path.save_flat_fn) {
127 self.domain_savers.insert(
128 entity_name,
129 DomainSavers {
130 save_fn,
131 save_flat_fn,
132 },
133 );
134 }
135
136 Ok(())
137 }
138
139 fn validate_migration_path(entity: &str, versions: &[String]) -> Result<(), MigrationError> {
141 Self::check_circular_path(entity, versions)?;
143
144 Self::check_version_ordering(entity, versions)?;
146
147 Ok(())
148 }
149
150 fn check_circular_path(entity: &str, versions: &[String]) -> Result<(), MigrationError> {
152 let mut seen = std::collections::HashSet::new();
153
154 for version in versions {
155 if !seen.insert(version) {
156 let path = versions.join(" -> ");
158 return Err(MigrationError::CircularMigrationPath {
159 entity: entity.to_string(),
160 path,
161 });
162 }
163 }
164
165 Ok(())
166 }
167
168 fn check_version_ordering(entity: &str, versions: &[String]) -> Result<(), MigrationError> {
170 for i in 0..versions.len().saturating_sub(1) {
171 let current = &versions[i];
172 let next = &versions[i + 1];
173
174 let current_ver = semver::Version::parse(current).map_err(|e| {
176 MigrationError::DeserializationError(format!("Invalid semver '{}': {}", current, e))
177 })?;
178
179 let next_ver = semver::Version::parse(next).map_err(|e| {
180 MigrationError::DeserializationError(format!("Invalid semver '{}': {}", next, e))
181 })?;
182
183 if next_ver <= current_ver {
185 return Err(MigrationError::InvalidVersionOrder {
186 entity: entity.to_string(),
187 from: current.clone(),
188 to: next.clone(),
189 });
190 }
191 }
192
193 Ok(())
194 }
195
196 pub fn load_from<D, T>(&self, entity: &str, data: T) -> Result<D, MigrationError>
229 where
230 D: DeserializeOwned,
231 T: Serialize,
232 {
233 let value = serde_json::to_value(data).map_err(|e| {
235 MigrationError::DeserializationError(format!(
236 "Failed to convert input data to internal format: {}",
237 e
238 ))
239 })?;
240
241 let path = self
243 .paths
244 .get(entity)
245 .ok_or_else(|| MigrationError::EntityNotFound(entity.to_string()))?;
246
247 let version_key = &path.version_key;
248 let data_key = &path.data_key;
249
250 let obj = value.as_object().ok_or_else(|| {
252 MigrationError::DeserializationError(
253 "Expected object with version and data fields".to_string(),
254 )
255 })?;
256
257 let current_version = obj
258 .get(version_key)
259 .and_then(|v| v.as_str())
260 .ok_or_else(|| {
261 MigrationError::DeserializationError(format!(
262 "Missing or invalid '{}' field",
263 version_key
264 ))
265 })?
266 .to_string();
267
268 let mut current_data = obj
269 .get(data_key)
270 .ok_or_else(|| {
271 MigrationError::DeserializationError(format!("Missing '{}' field", data_key))
272 })?
273 .clone();
274
275 let mut current_version = current_version;
276
277 while let Some(migrate_fn) = path.steps.get(¤t_version) {
279 current_data = migrate_fn(current_data.clone())?;
281
282 match path.versions.iter().position(|v| v == ¤t_version) {
285 Some(idx) if idx + 1 < path.versions.len() => {
286 current_version = path.versions[idx + 1].clone();
287 }
288 _ => break,
289 }
290 }
291
292 let domain_value = (path.finalize)(current_data)?;
294
295 serde_json::from_value(domain_value).map_err(|e| {
296 MigrationError::DeserializationError(format!("Failed to convert to domain: {}", e))
297 })
298 }
299
300 pub fn load<D: DeserializeOwned>(&self, entity: &str, json: &str) -> Result<D, MigrationError> {
328 let data: serde_json::Value = serde_json::from_str(json).map_err(|e| {
329 MigrationError::DeserializationError(format!("Failed to parse JSON: {}", e))
330 })?;
331 self.load_from(entity, data)
332 }
333
334 pub fn load_from_with_fallback<D, T>(&self, entity: &str, data: T) -> Result<D, MigrationError>
367 where
368 D: DeserializeOwned,
369 T: Serialize,
370 {
371 let value = serde_json::to_value(data).map_err(|e| {
373 MigrationError::DeserializationError(format!(
374 "Failed to convert input data to internal format: {}",
375 e
376 ))
377 })?;
378
379 let path = self
381 .paths
382 .get(entity)
383 .ok_or_else(|| MigrationError::EntityNotFound(entity.to_string()))?;
384
385 let version_key = &path.version_key;
386 let data_key = &path.data_key;
387
388 let (current_version, current_data) = if let Some(obj) = value.as_object() {
390 if let Some(version_value) = obj.get(version_key) {
391 if let Some(version_str) = version_value.as_str() {
392 let data = obj
394 .get(data_key)
395 .ok_or_else(|| {
396 MigrationError::DeserializationError(format!(
397 "Missing '{}' field",
398 data_key
399 ))
400 })?
401 .clone();
402 (version_str.to_string(), data)
403 } else {
404 if path.versions.is_empty() {
406 return Err(MigrationError::DeserializationError(
407 "No migration versions defined for fallback".to_string(),
408 ));
409 }
410 (path.versions[0].clone(), value)
411 }
412 } else {
413 if path.versions.is_empty() {
415 return Err(MigrationError::DeserializationError(
416 "No migration versions defined for fallback".to_string(),
417 ));
418 }
419 (path.versions[0].clone(), value)
420 }
421 } else {
422 return Err(MigrationError::DeserializationError(
423 "Expected object format for versioned data".to_string(),
424 ));
425 };
426
427 let mut current_version = current_version;
428 let mut current_data = current_data;
429
430 while let Some(migrate_fn) = path.steps.get(¤t_version) {
432 current_data = migrate_fn(current_data.clone())?;
434
435 match path.versions.iter().position(|v| v == ¤t_version) {
438 Some(idx) if idx + 1 < path.versions.len() => {
439 current_version = path.versions[idx + 1].clone();
440 }
441 _ => break,
442 }
443 }
444
445 let domain_value = (path.finalize)(current_data)?;
447
448 serde_json::from_value(domain_value).map_err(|e| {
449 MigrationError::DeserializationError(format!("Failed to convert to domain: {}", e))
450 })
451 }
452
453 pub fn load_with_fallback<D: DeserializeOwned>(
485 &self,
486 entity: &str,
487 json: &str,
488 ) -> Result<D, MigrationError> {
489 let data: serde_json::Value = serde_json::from_str(json).map_err(|e| {
490 MigrationError::DeserializationError(format!("Failed to parse JSON: {}", e))
491 })?;
492 self.load_from_with_fallback(entity, data)
493 }
494
495 pub fn load_flat<D: DeserializeOwned>(
523 &self,
524 entity: &str,
525 json: &str,
526 ) -> Result<D, MigrationError> {
527 let data: serde_json::Value = serde_json::from_str(json).map_err(|e| {
528 MigrationError::DeserializationError(format!("Failed to parse JSON: {}", e))
529 })?;
530 self.load_flat_from(entity, data)
531 }
532
533 pub fn load_flat_from<D, T>(&self, entity: &str, value: T) -> Result<D, MigrationError>
562 where
563 D: DeserializeOwned,
564 T: Serialize,
565 {
566 let path = self
567 .paths
568 .get(entity)
569 .ok_or_else(|| MigrationError::EntityNotFound(entity.to_string()))?;
570
571 let version_key = &path.version_key;
572
573 let mut value = serde_json::to_value(value).map_err(|e| {
575 MigrationError::SerializationError(format!("Failed to convert input: {}", e))
576 })?;
577
578 let obj = value.as_object_mut().ok_or_else(|| {
580 MigrationError::DeserializationError(
581 "Expected object with version field at top level".to_string(),
582 )
583 })?;
584
585 let current_version = obj
586 .remove(version_key)
587 .ok_or_else(|| {
588 MigrationError::DeserializationError(format!(
589 "Missing '{}' field in flat format",
590 version_key
591 ))
592 })?
593 .as_str()
594 .ok_or_else(|| {
595 MigrationError::DeserializationError(format!(
596 "Invalid '{}' field type",
597 version_key
598 ))
599 })?
600 .to_string();
601
602 let mut current_data = serde_json::Value::Object(obj.clone());
604 let mut current_version = current_version;
605
606 while let Some(migrate_fn) = path.steps.get(¤t_version) {
608 current_data = migrate_fn(current_data.clone())?;
610
611 match path.versions.iter().position(|v| v == ¤t_version) {
613 Some(idx) if idx + 1 < path.versions.len() => {
614 current_version = path.versions[idx + 1].clone();
615 }
616 _ => break,
617 }
618 }
619
620 let domain_value = (path.finalize)(current_data)?;
622
623 serde_json::from_value(domain_value).map_err(|e| {
624 MigrationError::DeserializationError(format!("Failed to convert to domain: {}", e))
625 })
626 }
627
628 pub fn save<T: Versioned + Serialize>(&self, data: T) -> Result<String, MigrationError> {
659 let version_key = T::VERSION_KEY;
661 let data_key = T::DATA_KEY;
662
663 let data_value = serde_json::to_value(&data).map_err(|e| {
665 MigrationError::SerializationError(format!("Failed to serialize data: {}", e))
666 })?;
667
668 let mut map = serde_json::Map::new();
670 map.insert(
671 version_key.to_string(),
672 serde_json::Value::String(T::VERSION.to_string()),
673 );
674 map.insert(data_key.to_string(), data_value);
675
676 serde_json::to_string(&map).map_err(|e| {
677 MigrationError::SerializationError(format!("Failed to serialize wrapper: {}", e))
678 })
679 }
680
681 pub fn save_flat<T: Versioned + Serialize>(&self, data: T) -> Result<String, MigrationError> {
711 let version_key = T::VERSION_KEY;
712
713 let mut data_value = serde_json::to_value(&data).map_err(|e| {
715 MigrationError::SerializationError(format!("Failed to serialize data: {}", e))
716 })?;
717
718 let obj = data_value.as_object_mut().ok_or_else(|| {
720 MigrationError::SerializationError(
721 "Data must serialize to a JSON object for flat format".to_string(),
722 )
723 })?;
724
725 obj.insert(
727 version_key.to_string(),
728 serde_json::Value::String(T::VERSION.to_string()),
729 );
730
731 serde_json::to_string(&obj).map_err(|e| {
732 MigrationError::SerializationError(format!("Failed to serialize flat format: {}", e))
733 })
734 }
735
736 pub fn load_vec_from<D, T>(&self, entity: &str, data: Vec<T>) -> Result<Vec<D>, MigrationError>
769 where
770 D: DeserializeOwned,
771 T: Serialize,
772 {
773 data.into_iter()
774 .map(|item| self.load_from(entity, item))
775 .collect()
776 }
777
778 pub fn load_vec<D: DeserializeOwned>(
809 &self,
810 entity: &str,
811 json: &str,
812 ) -> Result<Vec<D>, MigrationError> {
813 let data: Vec<serde_json::Value> = serde_json::from_str(json).map_err(|e| {
814 MigrationError::DeserializationError(format!("Failed to parse JSON array: {}", e))
815 })?;
816 self.load_vec_from(entity, data)
817 }
818
819 pub fn load_vec_flat<D: DeserializeOwned>(
850 &self,
851 entity: &str,
852 json: &str,
853 ) -> Result<Vec<D>, MigrationError> {
854 let data: Vec<serde_json::Value> = serde_json::from_str(json).map_err(|e| {
855 MigrationError::DeserializationError(format!("Failed to parse JSON array: {}", e))
856 })?;
857 self.load_vec_flat_from(entity, data)
858 }
859
860 pub fn load_vec_flat_from<D, T>(
889 &self,
890 entity: &str,
891 data: Vec<T>,
892 ) -> Result<Vec<D>, MigrationError>
893 where
894 D: DeserializeOwned,
895 T: Serialize,
896 {
897 data.into_iter()
898 .map(|item| self.load_flat_from(entity, item))
899 .collect()
900 }
901
902 pub fn save_vec<T: Versioned + Serialize>(
939 &self,
940 data: Vec<T>,
941 ) -> Result<String, MigrationError> {
942 let version_key = T::VERSION_KEY;
943 let data_key = T::DATA_KEY;
944
945 let wrappers: Result<Vec<serde_json::Value>, MigrationError> = data
946 .into_iter()
947 .map(|item| {
948 let data_value = serde_json::to_value(&item).map_err(|e| {
949 MigrationError::SerializationError(format!("Failed to serialize item: {}", e))
950 })?;
951
952 let mut map = serde_json::Map::new();
953 map.insert(
954 version_key.to_string(),
955 serde_json::Value::String(T::VERSION.to_string()),
956 );
957 map.insert(data_key.to_string(), data_value);
958
959 Ok(serde_json::Value::Object(map))
960 })
961 .collect();
962
963 serde_json::to_string(&wrappers?).map_err(|e| {
964 MigrationError::SerializationError(format!("Failed to serialize data array: {}", e))
965 })
966 }
967
968 pub fn save_vec_flat<T: Versioned + Serialize>(
1004 &self,
1005 data: Vec<T>,
1006 ) -> Result<String, MigrationError> {
1007 let version_key = T::VERSION_KEY;
1008
1009 let flat_items: Result<Vec<serde_json::Value>, MigrationError> = data
1010 .into_iter()
1011 .map(|item| {
1012 let mut data_value = serde_json::to_value(&item).map_err(|e| {
1013 MigrationError::SerializationError(format!("Failed to serialize item: {}", e))
1014 })?;
1015
1016 let obj = data_value.as_object_mut().ok_or_else(|| {
1017 MigrationError::SerializationError(
1018 "Data must serialize to a JSON object for flat format".to_string(),
1019 )
1020 })?;
1021
1022 obj.insert(
1024 version_key.to_string(),
1025 serde_json::Value::String(T::VERSION.to_string()),
1026 );
1027
1028 Ok(serde_json::Value::Object(obj.clone()))
1029 })
1030 .collect();
1031
1032 serde_json::to_string(&flat_items?).map_err(|e| {
1033 MigrationError::SerializationError(format!(
1034 "Failed to serialize flat data array: {}",
1035 e
1036 ))
1037 })
1038 }
1039
1040 pub fn load_forward<D: DeserializeOwned>(
1091 &self,
1092 entity: &str,
1093 json: &str,
1094 ) -> Result<Forwardable<D>, MigrationError> {
1095 let value: serde_json::Value = serde_json::from_str(json).map_err(|e| {
1096 MigrationError::DeserializationError(format!("Failed to parse JSON: {}", e))
1097 })?;
1098 self.load_forward_from(entity, value, false)
1099 }
1100
1101 pub fn load_forward_flat<D: DeserializeOwned>(
1113 &self,
1114 entity: &str,
1115 json: &str,
1116 ) -> Result<Forwardable<D>, MigrationError> {
1117 let value: serde_json::Value = serde_json::from_str(json).map_err(|e| {
1118 MigrationError::DeserializationError(format!("Failed to parse JSON: {}", e))
1119 })?;
1120 self.load_forward_from(entity, value, true)
1121 }
1122
1123 fn load_forward_from<D: DeserializeOwned>(
1125 &self,
1126 entity: &str,
1127 value: serde_json::Value,
1128 is_flat: bool,
1129 ) -> Result<Forwardable<D>, MigrationError> {
1130 let path = self
1131 .paths
1132 .get(entity)
1133 .ok_or_else(|| MigrationError::EntityNotFound(entity.to_string()))?;
1134
1135 let version_key = &path.version_key;
1136 let data_key = &path.data_key;
1137
1138 let obj = value.as_object().ok_or_else(|| {
1139 MigrationError::DeserializationError("Expected JSON object".to_string())
1140 })?;
1141
1142 let original_version = obj
1144 .get(version_key)
1145 .and_then(|v| v.as_str())
1146 .ok_or_else(|| {
1147 MigrationError::DeserializationError(format!(
1148 "Missing or invalid '{}' field",
1149 version_key
1150 ))
1151 })?
1152 .to_string();
1153
1154 let (data_value, all_fields) = if is_flat {
1156 let mut fields = obj.clone();
1158 fields.remove(version_key);
1159 (serde_json::Value::Object(fields.clone()), fields)
1160 } else {
1161 let data = obj
1163 .get(data_key)
1164 .ok_or_else(|| {
1165 MigrationError::DeserializationError(format!("Missing '{}' field", data_key))
1166 })?
1167 .clone();
1168 let fields = data
1169 .as_object()
1170 .cloned()
1171 .unwrap_or_else(serde_json::Map::new);
1172 (data, fields)
1173 };
1174
1175 let is_known_version = path.versions.contains(&original_version);
1177 let was_lossy = !is_known_version;
1178
1179 let target_version = if is_known_version {
1181 original_version.clone()
1182 } else {
1183 path.versions.last().cloned().ok_or_else(|| {
1185 MigrationError::DeserializationError(
1186 "No versions registered for entity".to_string(),
1187 )
1188 })?
1189 };
1190
1191 let mut current_data = data_value.clone();
1193 let mut current_version = if is_known_version {
1194 original_version.clone()
1195 } else {
1196 target_version.clone()
1198 };
1199
1200 if is_known_version {
1201 while let Some(migrate_fn) = path.steps.get(¤t_version) {
1203 current_data = migrate_fn(current_data)?;
1204 match path.versions.iter().position(|v| v == ¤t_version) {
1205 Some(idx) if idx + 1 < path.versions.len() => {
1206 current_version = path.versions[idx + 1].clone();
1207 }
1208 _ => break,
1209 }
1210 }
1211 }
1212
1213 let domain_value = (path.finalize)(current_data)?;
1215
1216 let domain: D = serde_json::from_value(domain_value.clone()).map_err(|e| {
1218 MigrationError::DeserializationError(format!("Failed to deserialize domain: {}", e))
1219 })?;
1220
1221 let domain_obj = domain_value
1223 .as_object()
1224 .cloned()
1225 .unwrap_or_else(serde_json::Map::new);
1226 let mut unknown_fields = serde_json::Map::new();
1227 for (key, value) in all_fields {
1228 if !domain_obj.contains_key(&key) {
1229 unknown_fields.insert(key, value);
1230 }
1231 }
1232
1233 let ctx = ForwardContext::new(
1234 original_version,
1235 unknown_fields,
1236 was_lossy,
1237 version_key.clone(),
1238 data_key.clone(),
1239 is_flat,
1240 );
1241
1242 Ok(Forwardable::new(domain, ctx))
1243 }
1244
1245 pub fn save_forward<D: Serialize>(
1266 &self,
1267 data: &Forwardable<D>,
1268 ) -> Result<String, MigrationError> {
1269 let ctx = data.context();
1270
1271 let mut domain_value = serde_json::to_value(&data.inner).map_err(|e| {
1273 MigrationError::SerializationError(format!("Failed to serialize domain: {}", e))
1274 })?;
1275
1276 if let Some(obj) = domain_value.as_object_mut() {
1278 for (key, value) in ctx.unknown_fields.iter() {
1279 if !obj.contains_key(key) {
1280 obj.insert(key.clone(), value.clone());
1281 }
1282 }
1283 }
1284
1285 if ctx.was_flat {
1287 let obj = domain_value.as_object_mut().ok_or_else(|| {
1289 MigrationError::SerializationError(
1290 "Domain must serialize to object for flat format".to_string(),
1291 )
1292 })?;
1293 obj.insert(
1294 ctx.version_key.clone(),
1295 serde_json::Value::String(ctx.original_version.clone()),
1296 );
1297 serde_json::to_string(&obj).map_err(|e| {
1298 MigrationError::SerializationError(format!("Failed to serialize: {}", e))
1299 })
1300 } else {
1301 let mut wrapper = serde_json::Map::new();
1303 wrapper.insert(
1304 ctx.version_key.clone(),
1305 serde_json::Value::String(ctx.original_version.clone()),
1306 );
1307 wrapper.insert(ctx.data_key.clone(), domain_value);
1308 serde_json::to_string(&wrapper).map_err(|e| {
1309 MigrationError::SerializationError(format!("Failed to serialize: {}", e))
1310 })
1311 }
1312 }
1313
1314 pub fn save_entity<E: crate::LatestVersioned>(
1352 &self,
1353 entity: E,
1354 ) -> Result<String, MigrationError> {
1355 let latest = entity.to_latest();
1356 self.save(latest)
1357 }
1358
1359 pub fn save_entity_flat<E: crate::LatestVersioned>(
1397 &self,
1398 entity: E,
1399 ) -> Result<String, MigrationError> {
1400 let latest = entity.to_latest();
1401 self.save_flat(latest)
1402 }
1403
1404 pub fn save_entity_vec<E: crate::LatestVersioned>(
1432 &self,
1433 entities: Vec<E>,
1434 ) -> Result<String, MigrationError> {
1435 let versioned: Vec<E::Latest> = entities.into_iter().map(|e| e.to_latest()).collect();
1436 self.save_vec(versioned)
1437 }
1438
1439 pub fn save_entity_vec_flat<E: crate::LatestVersioned>(
1467 &self,
1468 entities: Vec<E>,
1469 ) -> Result<String, MigrationError> {
1470 let versioned: Vec<E::Latest> = entities.into_iter().map(|e| e.to_latest()).collect();
1471 self.save_vec_flat(versioned)
1472 }
1473
1474 pub fn save_domain<T: Serialize>(
1512 &self,
1513 entity_name: &str,
1514 entity: T,
1515 ) -> Result<String, MigrationError> {
1516 let saver = self.domain_savers.get(entity_name).ok_or_else(|| {
1517 MigrationError::EntityNotFound(format!(
1518 "Entity '{}' is not registered with domain save support. Use into_with_save() when defining the migration path.",
1519 entity_name
1520 ))
1521 })?;
1522
1523 let path = self.paths.get(entity_name).ok_or_else(|| {
1525 MigrationError::EntityNotFound(format!("Entity '{}' is not registered", entity_name))
1526 })?;
1527
1528 let domain_value = serde_json::to_value(entity).map_err(|e| {
1529 MigrationError::SerializationError(format!("Failed to serialize entity: {}", e))
1530 })?;
1531
1532 (saver.save_fn)(domain_value, &path.version_key, &path.data_key)
1533 }
1534
1535 pub fn save_domain_flat<T: Serialize>(
1561 &self,
1562 entity_name: &str,
1563 entity: T,
1564 ) -> Result<String, MigrationError> {
1565 let saver = self.domain_savers.get(entity_name).ok_or_else(|| {
1566 MigrationError::EntityNotFound(format!(
1567 "Entity '{}' is not registered with domain save support. Use into_with_save() when defining the migration path.",
1568 entity_name
1569 ))
1570 })?;
1571
1572 let path = self.paths.get(entity_name).ok_or_else(|| {
1574 MigrationError::EntityNotFound(format!("Entity '{}' is not registered", entity_name))
1575 })?;
1576
1577 let domain_value = serde_json::to_value(entity).map_err(|e| {
1578 MigrationError::SerializationError(format!("Failed to serialize entity: {}", e))
1579 })?;
1580
1581 (saver.save_flat_fn)(domain_value, &path.version_key)
1582 }
1583}
1584
1585impl Default for Migrator {
1586 fn default() -> Self {
1587 Self::new()
1588 }
1589}
1590
1591pub struct MigratorBuilder {
1593 default_version_key: Option<String>,
1594 default_data_key: Option<String>,
1595}
1596
1597impl MigratorBuilder {
1598 pub(crate) fn new() -> Self {
1599 Self {
1600 default_version_key: None,
1601 default_data_key: None,
1602 }
1603 }
1604
1605 pub fn default_version_key(mut self, key: impl Into<String>) -> Self {
1611 self.default_version_key = Some(key.into());
1612 self
1613 }
1614
1615 pub fn default_data_key(mut self, key: impl Into<String>) -> Self {
1621 self.default_data_key = Some(key.into());
1622 self
1623 }
1624
1625 pub fn build(self) -> Migrator {
1627 Migrator {
1628 paths: HashMap::new(),
1629 default_version_key: self.default_version_key,
1630 default_data_key: self.default_data_key,
1631 domain_savers: HashMap::new(),
1632 }
1633 }
1634}
1635
1636pub struct Start;
1638
1639pub struct HasFrom<V>(PhantomData<V>);
1641
1642pub struct HasSteps<V>(PhantomData<V>);
1644
1645pub struct MigrationPathBuilder<State> {
1647 entity: String,
1648 steps: HashMap<String, MigrationFn>,
1649 versions: Vec<String>,
1650 version_key: String,
1651 data_key: String,
1652 custom_version_key: Option<String>,
1653 custom_data_key: Option<String>,
1654 _state: PhantomData<State>,
1655}
1656
1657impl MigrationPathBuilder<Start> {
1658 fn new(entity: String) -> Self {
1659 Self {
1660 entity,
1661 steps: HashMap::new(),
1662 versions: Vec::new(),
1663 version_key: String::from("version"),
1664 data_key: String::from("data"),
1665 custom_version_key: None,
1666 custom_data_key: None,
1667 _state: PhantomData,
1668 }
1669 }
1670
1671 pub fn with_keys(
1684 mut self,
1685 version_key: impl Into<String>,
1686 data_key: impl Into<String>,
1687 ) -> Self {
1688 self.custom_version_key = Some(version_key.into());
1689 self.custom_data_key = Some(data_key.into());
1690 self
1691 }
1692
1693 pub fn from<V: Versioned + DeserializeOwned>(self) -> MigrationPathBuilder<HasFrom<V>> {
1695 let mut versions = self.versions;
1696 versions.push(V::VERSION.to_string());
1697
1698 MigrationPathBuilder {
1699 entity: self.entity,
1700 steps: self.steps,
1701 versions,
1702 version_key: V::VERSION_KEY.to_string(),
1703 data_key: V::DATA_KEY.to_string(),
1704 custom_version_key: self.custom_version_key,
1705 custom_data_key: self.custom_data_key,
1706 _state: PhantomData,
1707 }
1708 }
1709}
1710
1711impl<V> MigrationPathBuilder<HasFrom<V>>
1712where
1713 V: Versioned + DeserializeOwned,
1714{
1715 pub fn step<Next>(mut self) -> MigrationPathBuilder<HasSteps<Next>>
1717 where
1718 V: MigratesTo<Next>,
1719 Next: Versioned + DeserializeOwned + Serialize,
1720 {
1721 let from_version = V::VERSION.to_string();
1722 let migration_fn: MigrationFn = Box::new(move |value| {
1723 let from_value: V = serde_json::from_value(value).map_err(|e| {
1724 MigrationError::DeserializationError(format!(
1725 "Failed to deserialize version {}: {}",
1726 V::VERSION,
1727 e
1728 ))
1729 })?;
1730
1731 let to_value = from_value.migrate();
1732
1733 serde_json::to_value(&to_value).map_err(|e| MigrationError::MigrationStepFailed {
1735 from: V::VERSION.to_string(),
1736 to: Next::VERSION.to_string(),
1737 error: e.to_string(),
1738 })
1739 });
1740
1741 self.steps.insert(from_version, migration_fn);
1742 self.versions.push(Next::VERSION.to_string());
1743
1744 MigrationPathBuilder {
1745 entity: self.entity,
1746 steps: self.steps,
1747 versions: self.versions,
1748 version_key: self.version_key,
1749 data_key: self.data_key,
1750 custom_version_key: self.custom_version_key,
1751 custom_data_key: self.custom_data_key,
1752 _state: PhantomData,
1753 }
1754 }
1755
1756 pub fn into<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1758 where
1759 V: IntoDomain<D>,
1760 {
1761 let finalize: Box<
1762 dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync,
1763 > = Box::new(move |value| {
1764 let versioned: V = serde_json::from_value(value).map_err(|e| {
1765 MigrationError::DeserializationError(format!(
1766 "Failed to deserialize final version: {}",
1767 e
1768 ))
1769 })?;
1770
1771 let domain = versioned.into_domain();
1772
1773 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1774 from: V::VERSION.to_string(),
1775 to: "domain".to_string(),
1776 error: e.to_string(),
1777 })
1778 });
1779
1780 MigrationPath {
1781 entity: self.entity,
1782 inner: EntityMigrationPath {
1783 steps: self.steps,
1784 finalize,
1785 versions: self.versions.clone(),
1786 version_key: self.version_key,
1787 data_key: self.data_key,
1788 },
1789 versions: self.versions,
1790 custom_version_key: self.custom_version_key,
1791 custom_data_key: self.custom_data_key,
1792 save_fn: None,
1793 save_flat_fn: None,
1794 _phantom: PhantomData,
1795 }
1796 }
1797
1798 pub fn into_with_save<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1833 where
1834 V: IntoDomain<D> + crate::FromDomain<D>,
1835 {
1836 let finalize: Box<
1837 dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync,
1838 > = Box::new(move |value| {
1839 let versioned: V = serde_json::from_value(value).map_err(|e| {
1840 MigrationError::DeserializationError(format!(
1841 "Failed to deserialize final version: {}",
1842 e
1843 ))
1844 })?;
1845
1846 let domain = versioned.into_domain();
1847
1848 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1849 from: V::VERSION.to_string(),
1850 to: "domain".to_string(),
1851 error: e.to_string(),
1852 })
1853 });
1854
1855 let version = V::VERSION;
1857
1858 let save_fn: DomainSaveFn = Box::new(move |domain_value, vkey, dkey| {
1859 let domain: D = serde_json::from_value(domain_value).map_err(|e| {
1860 MigrationError::DeserializationError(format!("Failed to deserialize domain: {}", e))
1861 })?;
1862
1863 let latest = V::from_domain(domain);
1864 let data_value = serde_json::to_value(&latest).map_err(|e| {
1865 MigrationError::SerializationError(format!("Failed to serialize latest: {}", e))
1866 })?;
1867
1868 let mut map = serde_json::Map::new();
1869 map.insert(
1870 vkey.to_string(),
1871 serde_json::Value::String(version.to_string()),
1872 );
1873 map.insert(dkey.to_string(), data_value);
1874
1875 serde_json::to_string(&map).map_err(|e| {
1876 MigrationError::SerializationError(format!("Failed to serialize wrapper: {}", e))
1877 })
1878 });
1879
1880 let save_flat_fn: DomainSaveFlatFn = Box::new(move |domain_value, vkey| {
1881 let domain: D = serde_json::from_value(domain_value).map_err(|e| {
1882 MigrationError::DeserializationError(format!("Failed to deserialize domain: {}", e))
1883 })?;
1884
1885 let latest = V::from_domain(domain);
1886 let mut data_value = serde_json::to_value(&latest).map_err(|e| {
1887 MigrationError::SerializationError(format!("Failed to serialize latest: {}", e))
1888 })?;
1889
1890 let obj = data_value.as_object_mut().ok_or_else(|| {
1891 MigrationError::SerializationError(
1892 "Data must serialize to a JSON object for flat format".to_string(),
1893 )
1894 })?;
1895
1896 obj.insert(
1897 vkey.to_string(),
1898 serde_json::Value::String(version.to_string()),
1899 );
1900
1901 serde_json::to_string(&obj).map_err(|e| {
1902 MigrationError::SerializationError(format!(
1903 "Failed to serialize flat format: {}",
1904 e
1905 ))
1906 })
1907 });
1908
1909 MigrationPath {
1910 entity: self.entity,
1911 inner: EntityMigrationPath {
1912 steps: self.steps,
1913 finalize,
1914 versions: self.versions.clone(),
1915 version_key: self.version_key,
1916 data_key: self.data_key,
1917 },
1918 versions: self.versions,
1919 custom_version_key: self.custom_version_key,
1920 custom_data_key: self.custom_data_key,
1921 save_fn: Some(save_fn),
1922 save_flat_fn: Some(save_flat_fn),
1923 _phantom: PhantomData,
1924 }
1925 }
1926}
1927
1928impl<V> MigrationPathBuilder<HasSteps<V>>
1929where
1930 V: Versioned + DeserializeOwned,
1931{
1932 pub fn step<Next>(mut self) -> MigrationPathBuilder<HasSteps<Next>>
1934 where
1935 V: MigratesTo<Next>,
1936 Next: Versioned + DeserializeOwned + Serialize,
1937 {
1938 let from_version = V::VERSION.to_string();
1939 let migration_fn: MigrationFn = Box::new(move |value| {
1940 let from_value: V = serde_json::from_value(value).map_err(|e| {
1941 MigrationError::DeserializationError(format!(
1942 "Failed to deserialize version {}: {}",
1943 V::VERSION,
1944 e
1945 ))
1946 })?;
1947
1948 let to_value = from_value.migrate();
1949
1950 serde_json::to_value(&to_value).map_err(|e| MigrationError::MigrationStepFailed {
1952 from: V::VERSION.to_string(),
1953 to: Next::VERSION.to_string(),
1954 error: e.to_string(),
1955 })
1956 });
1957
1958 self.steps.insert(from_version, migration_fn);
1959 self.versions.push(Next::VERSION.to_string());
1960
1961 MigrationPathBuilder {
1962 entity: self.entity,
1963 steps: self.steps,
1964 versions: self.versions,
1965 version_key: self.version_key,
1966 data_key: self.data_key,
1967 custom_version_key: self.custom_version_key,
1968 custom_data_key: self.custom_data_key,
1969 _state: PhantomData,
1970 }
1971 }
1972
1973 pub fn into<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1975 where
1976 V: IntoDomain<D>,
1977 {
1978 let finalize: Box<
1979 dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync,
1980 > = Box::new(move |value| {
1981 let versioned: V = serde_json::from_value(value).map_err(|e| {
1982 MigrationError::DeserializationError(format!(
1983 "Failed to deserialize final version: {}",
1984 e
1985 ))
1986 })?;
1987
1988 let domain = versioned.into_domain();
1989
1990 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1991 from: V::VERSION.to_string(),
1992 to: "domain".to_string(),
1993 error: e.to_string(),
1994 })
1995 });
1996
1997 MigrationPath {
1998 entity: self.entity,
1999 inner: EntityMigrationPath {
2000 steps: self.steps,
2001 finalize,
2002 versions: self.versions.clone(),
2003 version_key: self.version_key,
2004 data_key: self.data_key,
2005 },
2006 versions: self.versions,
2007 custom_version_key: self.custom_version_key,
2008 custom_data_key: self.custom_data_key,
2009 save_fn: None,
2010 save_flat_fn: None,
2011 _phantom: PhantomData,
2012 }
2013 }
2014
2015 pub fn into_with_save<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
2019 where
2020 V: IntoDomain<D> + crate::FromDomain<D>,
2021 {
2022 let finalize: Box<
2023 dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync,
2024 > = Box::new(move |value| {
2025 let versioned: V = serde_json::from_value(value).map_err(|e| {
2026 MigrationError::DeserializationError(format!(
2027 "Failed to deserialize final version: {}",
2028 e
2029 ))
2030 })?;
2031
2032 let domain = versioned.into_domain();
2033
2034 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
2035 from: V::VERSION.to_string(),
2036 to: "domain".to_string(),
2037 error: e.to_string(),
2038 })
2039 });
2040
2041 let version = V::VERSION;
2043
2044 let save_fn: DomainSaveFn = Box::new(move |domain_value, vkey, dkey| {
2045 let domain: D = serde_json::from_value(domain_value).map_err(|e| {
2046 MigrationError::DeserializationError(format!("Failed to deserialize domain: {}", e))
2047 })?;
2048
2049 let latest = V::from_domain(domain);
2050 let data_value = serde_json::to_value(&latest).map_err(|e| {
2051 MigrationError::SerializationError(format!("Failed to serialize latest: {}", e))
2052 })?;
2053
2054 let mut map = serde_json::Map::new();
2055 map.insert(
2056 vkey.to_string(),
2057 serde_json::Value::String(version.to_string()),
2058 );
2059 map.insert(dkey.to_string(), data_value);
2060
2061 serde_json::to_string(&map).map_err(|e| {
2062 MigrationError::SerializationError(format!("Failed to serialize wrapper: {}", e))
2063 })
2064 });
2065
2066 let save_flat_fn: DomainSaveFlatFn = Box::new(move |domain_value, vkey| {
2067 let domain: D = serde_json::from_value(domain_value).map_err(|e| {
2068 MigrationError::DeserializationError(format!("Failed to deserialize domain: {}", e))
2069 })?;
2070
2071 let latest = V::from_domain(domain);
2072 let mut data_value = serde_json::to_value(&latest).map_err(|e| {
2073 MigrationError::SerializationError(format!("Failed to serialize latest: {}", e))
2074 })?;
2075
2076 let obj = data_value.as_object_mut().ok_or_else(|| {
2077 MigrationError::SerializationError(
2078 "Data must serialize to a JSON object for flat format".to_string(),
2079 )
2080 })?;
2081
2082 obj.insert(
2083 vkey.to_string(),
2084 serde_json::Value::String(version.to_string()),
2085 );
2086
2087 serde_json::to_string(&obj).map_err(|e| {
2088 MigrationError::SerializationError(format!(
2089 "Failed to serialize flat format: {}",
2090 e
2091 ))
2092 })
2093 });
2094
2095 MigrationPath {
2096 entity: self.entity,
2097 inner: EntityMigrationPath {
2098 steps: self.steps,
2099 finalize,
2100 versions: self.versions.clone(),
2101 version_key: self.version_key,
2102 data_key: self.data_key,
2103 },
2104 versions: self.versions,
2105 custom_version_key: self.custom_version_key,
2106 custom_data_key: self.custom_data_key,
2107 save_fn: Some(save_fn),
2108 save_flat_fn: Some(save_flat_fn),
2109 _phantom: PhantomData,
2110 }
2111 }
2112}
2113
2114pub struct MigrationPath<D> {
2116 entity: String,
2117 inner: EntityMigrationPath,
2118 versions: Vec<String>,
2120 custom_version_key: Option<String>,
2122 custom_data_key: Option<String>,
2124 save_fn: Option<DomainSaveFn>,
2126 save_flat_fn: Option<DomainSaveFlatFn>,
2128 _phantom: PhantomData<D>,
2129}
2130
2131pub struct ConfigMigrator {
2166 root: serde_json::Value,
2167 migrator: Migrator,
2168}
2169
2170impl ConfigMigrator {
2171 pub fn from(json: &str, migrator: Migrator) -> Result<Self, MigrationError> {
2177 let root = serde_json::from_str(json)
2178 .map_err(|e| MigrationError::DeserializationError(e.to_string()))?;
2179 Ok(Self { root, migrator })
2180 }
2181
2182 pub fn query<T>(&self, key: &str) -> Result<Vec<T>, MigrationError>
2202 where
2203 T: crate::Queryable + for<'de> serde::Deserialize<'de>,
2204 {
2205 let value = &self.root[key];
2206 if value.is_null() {
2207 return Ok(Vec::new());
2208 }
2209
2210 if !value.is_array() {
2211 return Err(MigrationError::DeserializationError(format!(
2212 "Key '{}' does not contain an array",
2213 key
2214 )));
2215 }
2216
2217 match value.as_array() {
2218 Some(array) => self
2219 .migrator
2220 .load_vec_flat_from(T::ENTITY_NAME, array.to_vec()),
2221 None => Err(MigrationError::DeserializationError(format!(
2222 "Key '{}' is not an array",
2223 key
2224 ))),
2225 }
2226 }
2227
2228 pub fn update<T>(&mut self, key: &str, data: Vec<T>) -> Result<(), MigrationError>
2249 where
2250 T: serde::Serialize + crate::Queryable,
2251 {
2252 let entity_name = T::ENTITY_NAME;
2253 let latest_version = self
2254 .migrator
2255 .get_latest_version(entity_name)
2256 .ok_or_else(|| MigrationError::EntityNotFound(entity_name.to_string()))?;
2257
2258 let items: Vec<serde_json::Value> = data
2260 .into_iter()
2261 .map(|item| {
2262 let mut obj = serde_json::to_value(&item)
2263 .map_err(|e| MigrationError::SerializationError(e.to_string()))?;
2264
2265 if let Some(obj_map) = obj.as_object_mut() {
2266 obj_map.insert(
2267 "version".to_string(),
2268 serde_json::Value::String(latest_version.to_string()),
2269 );
2270 }
2271
2272 Ok(obj)
2273 })
2274 .collect::<Result<Vec<_>, MigrationError>>()?;
2275
2276 self.root[key] = serde_json::Value::Array(items);
2277 Ok(())
2278 }
2279
2280 pub fn to_string(&self) -> Result<String, MigrationError> {
2286 serde_json::to_string_pretty(&self.root)
2287 .map_err(|e| MigrationError::SerializationError(e.to_string()))
2288 }
2289
2290 pub fn to_string_compact(&self) -> Result<String, MigrationError> {
2296 serde_json::to_string(&self.root)
2297 .map_err(|e| MigrationError::SerializationError(e.to_string()))
2298 }
2299
2300 pub fn as_value(&self) -> &serde_json::Value {
2302 &self.root
2303 }
2304}
2305
2306#[cfg(test)]
2307mod tests {
2308 use super::*;
2309 use crate::{IntoDomain, MigratesTo, Versioned, VersionedWrapper};
2310 use serde::{Deserialize, Serialize};
2311
2312 #[derive(Serialize, Deserialize, Debug, PartialEq)]
2314 struct V1 {
2315 value: String,
2316 }
2317
2318 impl Versioned for V1 {
2319 const VERSION: &'static str = "1.0.0";
2320 }
2321
2322 #[derive(Serialize, Deserialize, Debug, PartialEq)]
2323 struct V2 {
2324 value: String,
2325 count: u32,
2326 }
2327
2328 impl Versioned for V2 {
2329 const VERSION: &'static str = "2.0.0";
2330 }
2331
2332 #[derive(Serialize, Deserialize, Debug, PartialEq)]
2333 struct V3 {
2334 value: String,
2335 count: u32,
2336 enabled: bool,
2337 }
2338
2339 impl Versioned for V3 {
2340 const VERSION: &'static str = "3.0.0";
2341 }
2342
2343 #[derive(Serialize, Deserialize, Debug, PartialEq)]
2344 struct Domain {
2345 value: String,
2346 count: u32,
2347 enabled: bool,
2348 }
2349
2350 impl MigratesTo<V2> for V1 {
2351 fn migrate(self) -> V2 {
2352 V2 {
2353 value: self.value,
2354 count: 0,
2355 }
2356 }
2357 }
2358
2359 impl MigratesTo<V3> for V2 {
2360 fn migrate(self) -> V3 {
2361 V3 {
2362 value: self.value,
2363 count: self.count,
2364 enabled: true,
2365 }
2366 }
2367 }
2368
2369 impl IntoDomain<Domain> for V3 {
2370 fn into_domain(self) -> Domain {
2371 Domain {
2372 value: self.value,
2373 count: self.count,
2374 enabled: self.enabled,
2375 }
2376 }
2377 }
2378
2379 #[test]
2380 fn test_migrator_new() {
2381 let migrator = Migrator::new();
2382 assert_eq!(migrator.paths.len(), 0);
2383 }
2384
2385 #[test]
2386 fn test_migrator_default() {
2387 let migrator = Migrator::default();
2388 assert_eq!(migrator.paths.len(), 0);
2389 }
2390
2391 #[test]
2392 fn test_single_step_migration() {
2393 let path = Migrator::define("test")
2394 .from::<V2>()
2395 .step::<V3>()
2396 .into::<Domain>();
2397
2398 let mut migrator = Migrator::new();
2399 migrator.register(path).unwrap();
2400
2401 let v2 = V2 {
2402 value: "test".to_string(),
2403 count: 42,
2404 };
2405 let wrapper = VersionedWrapper::from_versioned(v2);
2406 let json = serde_json::to_string(&wrapper).unwrap();
2407
2408 let result: Domain = migrator.load("test", &json).unwrap();
2409 assert_eq!(result.value, "test");
2410 assert_eq!(result.count, 42);
2411 assert!(result.enabled);
2412 }
2413
2414 #[test]
2415 fn test_multi_step_migration() {
2416 let path = Migrator::define("test")
2417 .from::<V1>()
2418 .step::<V2>()
2419 .step::<V3>()
2420 .into::<Domain>();
2421
2422 let mut migrator = Migrator::new();
2423 migrator.register(path).unwrap();
2424
2425 let v1 = V1 {
2426 value: "multi_step".to_string(),
2427 };
2428 let wrapper = VersionedWrapper::from_versioned(v1);
2429 let json = serde_json::to_string(&wrapper).unwrap();
2430
2431 let result: Domain = migrator.load("test", &json).unwrap();
2432 assert_eq!(result.value, "multi_step");
2433 assert_eq!(result.count, 0);
2434 assert!(result.enabled);
2435 }
2436
2437 #[test]
2438 fn test_no_migration_needed() {
2439 let path = Migrator::define("test").from::<V3>().into::<Domain>();
2440
2441 let mut migrator = Migrator::new();
2442 migrator.register(path).unwrap();
2443
2444 let v3 = V3 {
2445 value: "latest".to_string(),
2446 count: 100,
2447 enabled: false,
2448 };
2449 let wrapper = VersionedWrapper::from_versioned(v3);
2450 let json = serde_json::to_string(&wrapper).unwrap();
2451
2452 let result: Domain = migrator.load("test", &json).unwrap();
2453 assert_eq!(result.value, "latest");
2454 assert_eq!(result.count, 100);
2455 assert!(!result.enabled);
2456 }
2457
2458 #[test]
2459 fn test_entity_not_found() {
2460 let migrator = Migrator::new();
2461
2462 let v1 = V1 {
2463 value: "test".to_string(),
2464 };
2465 let wrapper = VersionedWrapper::from_versioned(v1);
2466 let json = serde_json::to_string(&wrapper).unwrap();
2467
2468 let result: Result<Domain, MigrationError> = migrator.load("unknown", &json);
2469 assert!(matches!(result, Err(MigrationError::EntityNotFound(_))));
2470
2471 if let Err(MigrationError::EntityNotFound(entity)) = result {
2472 assert_eq!(entity, "unknown");
2473 }
2474 }
2475
2476 #[test]
2477 fn test_invalid_json() {
2478 let path = Migrator::define("test").from::<V3>().into::<Domain>();
2479
2480 let mut migrator = Migrator::new();
2481 migrator.register(path).unwrap();
2482
2483 let invalid_json = "{ invalid json }";
2484 let result: Result<Domain, MigrationError> = migrator.load("test", invalid_json);
2485
2486 assert!(matches!(
2487 result,
2488 Err(MigrationError::DeserializationError(_))
2489 ));
2490 }
2491
2492 #[test]
2493 fn test_multiple_entities() {
2494 #[derive(Serialize, Deserialize, Debug, PartialEq)]
2495 struct OtherDomain {
2496 value: String,
2497 }
2498
2499 impl IntoDomain<OtherDomain> for V1 {
2500 fn into_domain(self) -> OtherDomain {
2501 OtherDomain { value: self.value }
2502 }
2503 }
2504
2505 let path1 = Migrator::define("entity1")
2506 .from::<V1>()
2507 .step::<V2>()
2508 .step::<V3>()
2509 .into::<Domain>();
2510
2511 let path2 = Migrator::define("entity2")
2512 .from::<V1>()
2513 .into::<OtherDomain>();
2514
2515 let mut migrator = Migrator::new();
2516 migrator.register(path1).unwrap();
2517 migrator.register(path2).unwrap();
2518
2519 let v1 = V1 {
2521 value: "entity1".to_string(),
2522 };
2523 let wrapper = VersionedWrapper::from_versioned(v1);
2524 let json = serde_json::to_string(&wrapper).unwrap();
2525 let result: Domain = migrator.load("entity1", &json).unwrap();
2526 assert_eq!(result.value, "entity1");
2527
2528 let v1 = V1 {
2530 value: "entity2".to_string(),
2531 };
2532 let wrapper = VersionedWrapper::from_versioned(v1);
2533 let json = serde_json::to_string(&wrapper).unwrap();
2534 let result: OtherDomain = migrator.load("entity2", &json).unwrap();
2535 assert_eq!(result.value, "entity2");
2536 }
2537
2538 #[test]
2539 fn test_save() {
2540 let migrator = Migrator::new();
2541
2542 let v1 = V1 {
2543 value: "test_save".to_string(),
2544 };
2545
2546 let json = migrator.save(v1).unwrap();
2547
2548 assert!(json.contains("\"version\""));
2550 assert!(json.contains("\"1.0.0\""));
2551 assert!(json.contains("\"data\""));
2552 assert!(json.contains("\"test_save\""));
2553
2554 let parsed: VersionedWrapper<serde_json::Value> = serde_json::from_str(&json).unwrap();
2556 assert_eq!(parsed.version, "1.0.0");
2557 }
2558
2559 #[test]
2560 fn test_save_and_load_roundtrip() {
2561 let path = Migrator::define("test")
2562 .from::<V1>()
2563 .step::<V2>()
2564 .step::<V3>()
2565 .into::<Domain>();
2566
2567 let mut migrator = Migrator::new();
2568 migrator.register(path).unwrap();
2569
2570 let v1 = V1 {
2572 value: "roundtrip".to_string(),
2573 };
2574 let json = migrator.save(v1).unwrap();
2575
2576 let domain: Domain = migrator.load("test", &json).unwrap();
2578
2579 assert_eq!(domain.value, "roundtrip");
2580 assert_eq!(domain.count, 0); assert!(domain.enabled); }
2583
2584 #[test]
2585 fn test_save_latest_version() {
2586 let migrator = Migrator::new();
2587
2588 let v3 = V3 {
2589 value: "latest".to_string(),
2590 count: 42,
2591 enabled: false,
2592 };
2593
2594 let json = migrator.save(v3).unwrap();
2595
2596 assert!(json.contains("\"version\":\"3.0.0\""));
2598 assert!(json.contains("\"value\":\"latest\""));
2599 assert!(json.contains("\"count\":42"));
2600 assert!(json.contains("\"enabled\":false"));
2601 }
2602
2603 #[test]
2604 fn test_save_pretty() {
2605 let migrator = Migrator::new();
2606
2607 let v2 = V2 {
2608 value: "pretty".to_string(),
2609 count: 10,
2610 };
2611
2612 let json = migrator.save(v2).unwrap();
2613
2614 assert!(!json.contains('\n'));
2616 assert!(json.contains("\"version\":\"2.0.0\""));
2617 }
2618
2619 #[test]
2620 fn test_validation_invalid_version_order() {
2621 let entity = "test".to_string();
2623 let versions = vec!["2.0.0".to_string(), "1.0.0".to_string()]; let result = Migrator::validate_migration_path(&entity, &versions);
2626 assert!(matches!(
2627 result,
2628 Err(MigrationError::InvalidVersionOrder { .. })
2629 ));
2630
2631 if let Err(MigrationError::InvalidVersionOrder {
2632 entity: e,
2633 from,
2634 to,
2635 }) = result
2636 {
2637 assert_eq!(e, "test");
2638 assert_eq!(from, "2.0.0");
2639 assert_eq!(to, "1.0.0");
2640 }
2641 }
2642
2643 #[test]
2644 fn test_validation_circular_path() {
2645 let entity = "test".to_string();
2647 let versions = vec![
2648 "1.0.0".to_string(),
2649 "2.0.0".to_string(),
2650 "1.0.0".to_string(), ];
2652
2653 let result = Migrator::validate_migration_path(&entity, &versions);
2654 assert!(matches!(
2655 result,
2656 Err(MigrationError::CircularMigrationPath { .. })
2657 ));
2658
2659 if let Err(MigrationError::CircularMigrationPath { entity: e, path }) = result {
2660 assert_eq!(e, "test");
2661 assert!(path.contains("1.0.0"));
2662 assert!(path.contains("2.0.0"));
2663 }
2664 }
2665
2666 #[test]
2667 fn test_validation_valid_path() {
2668 let entity = "test".to_string();
2670 let versions = vec![
2671 "1.0.0".to_string(),
2672 "1.1.0".to_string(),
2673 "2.0.0".to_string(),
2674 ];
2675
2676 let result = Migrator::validate_migration_path(&entity, &versions);
2677 assert!(result.is_ok());
2678 }
2679
2680 #[test]
2681 fn test_validation_empty_path() {
2682 let entity = "test".to_string();
2684 let versions = vec![];
2685
2686 let result = Migrator::validate_migration_path(&entity, &versions);
2687 assert!(result.is_ok());
2688 }
2689
2690 #[test]
2691 fn test_validation_single_version() {
2692 let entity = "test".to_string();
2694 let versions = vec!["1.0.0".to_string()];
2695
2696 let result = Migrator::validate_migration_path(&entity, &versions);
2697 assert!(result.is_ok());
2698 }
2699
2700 #[test]
2702 fn test_save_vec_and_load_vec() {
2703 let migrator = Migrator::new();
2704
2705 let items = vec![
2707 V1 {
2708 value: "item1".to_string(),
2709 },
2710 V1 {
2711 value: "item2".to_string(),
2712 },
2713 V1 {
2714 value: "item3".to_string(),
2715 },
2716 ];
2717
2718 let json = migrator.save_vec(items).unwrap();
2719
2720 assert!(json.starts_with('['));
2722 assert!(json.ends_with(']'));
2723 assert!(json.contains("\"version\":\"1.0.0\""));
2724 assert!(json.contains("item1"));
2725 assert!(json.contains("item2"));
2726 assert!(json.contains("item3"));
2727
2728 let path = Migrator::define("test")
2730 .from::<V1>()
2731 .step::<V2>()
2732 .step::<V3>()
2733 .into::<Domain>();
2734
2735 let mut migrator = Migrator::new();
2736 migrator.register(path).unwrap();
2737
2738 let domains: Vec<Domain> = migrator.load_vec("test", &json).unwrap();
2740
2741 assert_eq!(domains.len(), 3);
2742 assert_eq!(domains[0].value, "item1");
2743 assert_eq!(domains[1].value, "item2");
2744 assert_eq!(domains[2].value, "item3");
2745
2746 for domain in &domains {
2748 assert_eq!(domain.count, 0);
2749 assert!(domain.enabled);
2750 }
2751 }
2752
2753 #[test]
2754 fn test_load_vec_empty_array() {
2755 let path = Migrator::define("test")
2756 .from::<V1>()
2757 .step::<V2>()
2758 .step::<V3>()
2759 .into::<Domain>();
2760
2761 let mut migrator = Migrator::new();
2762 migrator.register(path).unwrap();
2763
2764 let json = "[]";
2765 let domains: Vec<Domain> = migrator.load_vec("test", json).unwrap();
2766
2767 assert_eq!(domains.len(), 0);
2768 }
2769
2770 #[test]
2771 fn test_load_vec_mixed_versions() {
2772 let path = Migrator::define("test")
2774 .from::<V1>()
2775 .step::<V2>()
2776 .step::<V3>()
2777 .into::<Domain>();
2778
2779 let mut migrator = Migrator::new();
2780 migrator.register(path).unwrap();
2781
2782 let json = r#"[
2784 {"version":"1.0.0","data":{"value":"v1-item"}},
2785 {"version":"2.0.0","data":{"value":"v2-item","count":42}},
2786 {"version":"3.0.0","data":{"value":"v3-item","count":99,"enabled":false}}
2787 ]"#;
2788
2789 let domains: Vec<Domain> = migrator.load_vec("test", json).unwrap();
2790
2791 assert_eq!(domains.len(), 3);
2792
2793 assert_eq!(domains[0].value, "v1-item");
2795 assert_eq!(domains[0].count, 0);
2796 assert!(domains[0].enabled);
2797
2798 assert_eq!(domains[1].value, "v2-item");
2800 assert_eq!(domains[1].count, 42);
2801 assert!(domains[1].enabled);
2802
2803 assert_eq!(domains[2].value, "v3-item");
2805 assert_eq!(domains[2].count, 99);
2806 assert!(!domains[2].enabled);
2807 }
2808
2809 #[test]
2810 fn test_load_vec_from_json_values() {
2811 let path = Migrator::define("test")
2812 .from::<V1>()
2813 .step::<V2>()
2814 .step::<V3>()
2815 .into::<Domain>();
2816
2817 let mut migrator = Migrator::new();
2818 migrator.register(path).unwrap();
2819
2820 let values: Vec<serde_json::Value> = vec![
2822 serde_json::json!({"version":"1.0.0","data":{"value":"direct1"}}),
2823 serde_json::json!({"version":"1.0.0","data":{"value":"direct2"}}),
2824 ];
2825
2826 let domains: Vec<Domain> = migrator.load_vec_from("test", values).unwrap();
2827
2828 assert_eq!(domains.len(), 2);
2829 assert_eq!(domains[0].value, "direct1");
2830 assert_eq!(domains[1].value, "direct2");
2831 }
2832
2833 #[test]
2834 fn test_save_vec_empty() {
2835 let migrator = Migrator::new();
2836 let empty: Vec<V1> = vec![];
2837
2838 let json = migrator.save_vec(empty).unwrap();
2839
2840 assert_eq!(json, "[]");
2841 }
2842
2843 #[test]
2844 fn test_load_vec_invalid_json() {
2845 let path = Migrator::define("test")
2846 .from::<V1>()
2847 .step::<V2>()
2848 .step::<V3>()
2849 .into::<Domain>();
2850
2851 let mut migrator = Migrator::new();
2852 migrator.register(path).unwrap();
2853
2854 let invalid_json = "{ not an array }";
2855 let result: Result<Vec<Domain>, MigrationError> = migrator.load_vec("test", invalid_json);
2856
2857 assert!(matches!(
2858 result,
2859 Err(MigrationError::DeserializationError(_))
2860 ));
2861 }
2862
2863 #[test]
2864 fn test_load_vec_entity_not_found() {
2865 let migrator = Migrator::new();
2866
2867 let json = r#"[{"version":"1.0.0","data":{"value":"test"}}]"#;
2868 let result: Result<Vec<Domain>, MigrationError> = migrator.load_vec("unknown", json);
2869
2870 assert!(matches!(result, Err(MigrationError::EntityNotFound(_))));
2871 }
2872
2873 #[test]
2874 fn test_save_vec_latest_version() {
2875 let migrator = Migrator::new();
2876
2877 let items = vec![
2878 V3 {
2879 value: "latest1".to_string(),
2880 count: 10,
2881 enabled: true,
2882 },
2883 V3 {
2884 value: "latest2".to_string(),
2885 count: 20,
2886 enabled: false,
2887 },
2888 ];
2889
2890 let json = migrator.save_vec(items).unwrap();
2891
2892 assert!(json.contains("\"version\":\"3.0.0\""));
2894 assert!(json.contains("latest1"));
2895 assert!(json.contains("latest2"));
2896 assert!(json.contains("\"count\":10"));
2897 assert!(json.contains("\"count\":20"));
2898 }
2899
2900 #[test]
2901 fn test_load_with_fallback_versioned_data() {
2902 let path = Migrator::define("test")
2903 .from::<V1>()
2904 .step::<V2>()
2905 .step::<V3>()
2906 .into::<Domain>();
2907
2908 let mut migrator = Migrator::new();
2909 migrator.register(path).unwrap();
2910
2911 let versioned_json = r#"{"version":"1.0.0","data":{"value":"versioned_test"}}"#;
2913 let result: Domain = migrator.load_with_fallback("test", versioned_json).unwrap();
2914
2915 assert_eq!(result.value, "versioned_test");
2916 assert_eq!(result.count, 0); assert!(result.enabled); }
2919
2920 #[test]
2921 fn test_load_with_fallback_legacy_data() {
2922 let path = Migrator::define("test")
2923 .from::<V1>()
2924 .step::<V2>()
2925 .step::<V3>()
2926 .into::<Domain>();
2927
2928 let mut migrator = Migrator::new();
2929 migrator.register(path).unwrap();
2930
2931 let legacy_json = r#"{"value":"legacy_test"}"#;
2933 let result: Domain = migrator.load_with_fallback("test", legacy_json).unwrap();
2934
2935 assert_eq!(result.value, "legacy_test");
2936 assert_eq!(result.count, 0); assert!(result.enabled); }
2939
2940 #[test]
2941 fn test_load_with_fallback_mixed_formats() {
2942 let path = Migrator::define("test")
2943 .from::<V2>()
2944 .step::<V3>()
2945 .into::<Domain>();
2946
2947 let mut migrator = Migrator::new();
2948 migrator.register(path).unwrap();
2949
2950 let legacy_v2_json = r#"{"value":"legacy_v2","count":42}"#;
2952 let result: Domain = migrator.load_with_fallback("test", legacy_v2_json).unwrap();
2953
2954 assert_eq!(result.value, "legacy_v2");
2955 assert_eq!(result.count, 42);
2956 assert!(result.enabled); let versioned_json = r#"{"version":"2.0.0","data":{"value":"versioned_v2","count":99}}"#;
2960 let result2: Domain = migrator.load_with_fallback("test", versioned_json).unwrap();
2961
2962 assert_eq!(result2.value, "versioned_v2");
2963 assert_eq!(result2.count, 99);
2964 assert!(result2.enabled);
2965 }
2966
2967 #[test]
2968 fn test_load_from_with_fallback_toml_value() {
2969 let path = Migrator::define("test")
2970 .from::<V1>()
2971 .step::<V2>()
2972 .step::<V3>()
2973 .into::<Domain>();
2974
2975 let mut migrator = Migrator::new();
2976 migrator.register(path).unwrap();
2977
2978 let data = serde_json::json!({"value": "from_toml"});
2980 let result: Domain = migrator.load_from_with_fallback("test", data).unwrap();
2981
2982 assert_eq!(result.value, "from_toml");
2983 assert_eq!(result.count, 0);
2984 assert!(result.enabled);
2985 }
2986
2987 #[test]
2988 fn test_load_with_fallback_no_migration_path() {
2989 let migrator = Migrator::new();
2990
2991 let legacy_json = r#"{"value":"test"}"#;
2992 let result: Result<Domain, MigrationError> =
2993 migrator.load_with_fallback("unknown", legacy_json);
2994
2995 assert!(matches!(result, Err(MigrationError::EntityNotFound(_))));
2996 }
2997
2998 #[test]
2999 fn test_load_with_fallback_empty_migration_path() {
3000 let path = Migrator::define("test").from::<V3>().into::<Domain>();
3002
3003 let mut migrator = Migrator::new();
3004 migrator.register(path).unwrap();
3005
3006 let legacy_json = r#"{"value":"direct","count":10,"enabled":false}"#;
3008 let result: Domain = migrator.load_with_fallback("test", legacy_json).unwrap();
3009
3010 assert_eq!(result.value, "direct");
3011 assert_eq!(result.count, 10);
3012 assert!(!result.enabled);
3013 }
3014
3015 #[test]
3016 fn test_load_with_fallback_invalid_json() {
3017 let path = Migrator::define("test")
3018 .from::<V1>()
3019 .step::<V2>()
3020 .step::<V3>()
3021 .into::<Domain>();
3022
3023 let mut migrator = Migrator::new();
3024 migrator.register(path).unwrap();
3025
3026 let invalid_json = "{ invalid json }";
3027 let result: Result<Domain, MigrationError> =
3028 migrator.load_with_fallback("test", invalid_json);
3029
3030 assert!(matches!(
3031 result,
3032 Err(MigrationError::DeserializationError(_))
3033 ));
3034 }
3035
3036 #[test]
3037 fn test_load_with_fallback_non_object_data() {
3038 let path = Migrator::define("test")
3039 .from::<V1>()
3040 .step::<V2>()
3041 .step::<V3>()
3042 .into::<Domain>();
3043
3044 let mut migrator = Migrator::new();
3045 migrator.register(path).unwrap();
3046
3047 let array_json = r#"["not", "an", "object"]"#;
3049 let result: Result<Domain, MigrationError> =
3050 migrator.load_with_fallback("test", array_json);
3051
3052 assert!(matches!(
3053 result,
3054 Err(MigrationError::DeserializationError(_))
3055 ));
3056 }
3057
3058 #[test]
3059 fn test_invalid_semver_version() {
3060 #[derive(Serialize, Deserialize, Debug)]
3063 struct BadV1 {
3064 value: String,
3065 }
3066
3067 impl Versioned for BadV1 {
3068 const VERSION: &'static str = "not-a-semver";
3069 }
3070
3071 impl MigratesTo<V2> for BadV1 {
3072 fn migrate(self) -> V2 {
3073 V2 {
3074 value: self.value,
3075 count: 0,
3076 }
3077 }
3078 }
3079
3080 let path = Migrator::define("bad")
3082 .from::<BadV1>()
3083 .step::<V2>()
3084 .step::<V3>()
3085 .into::<Domain>();
3086
3087 let mut migrator = Migrator::new();
3088 let result = migrator.register(path);
3089
3090 assert!(matches!(
3092 result,
3093 Err(MigrationError::DeserializationError(_))
3094 ));
3095 }
3096
3097 #[test]
3098 fn test_load_from_with_toml_value() {
3099 let path = Migrator::define("test")
3100 .from::<V1>()
3101 .step::<V2>()
3102 .step::<V3>()
3103 .into::<Domain>();
3104
3105 let mut migrator = Migrator::new();
3106 migrator.register(path).unwrap();
3107
3108 let toml_str = r#"
3110 version = "1.0.0"
3111 [data]
3112 value = "from_toml"
3113 "#;
3114 let toml_value: toml::Value = toml::from_str(toml_str).unwrap();
3115
3116 let result: Domain = migrator.load_from("test", toml_value).unwrap();
3117 assert_eq!(result.value, "from_toml");
3118 assert_eq!(result.count, 0);
3119 assert!(result.enabled);
3120 }
3121
3122 #[test]
3123 fn test_load_from_missing_version_field() {
3124 let path = Migrator::define("test").from::<V3>().into::<Domain>();
3125
3126 let mut migrator = Migrator::new();
3127 migrator.register(path).unwrap();
3128
3129 let json_value: serde_json::Value = serde_json::json!({
3131 "data": {"value": "test", "count": 1, "enabled": true}
3132 });
3133
3134 let result: Result<Domain, MigrationError> = migrator.load_from("test", json_value);
3135 assert!(matches!(
3136 result,
3137 Err(MigrationError::DeserializationError(_))
3138 ));
3139 }
3140
3141 #[test]
3142 fn test_load_from_missing_data_field() {
3143 let path = Migrator::define("test").from::<V3>().into::<Domain>();
3144
3145 let mut migrator = Migrator::new();
3146 migrator.register(path).unwrap();
3147
3148 let json_value: serde_json::Value = serde_json::json!({
3150 "version": "3.0.0"
3151 });
3152
3153 let result: Result<Domain, MigrationError> = migrator.load_from("test", json_value);
3154 assert!(matches!(
3155 result,
3156 Err(MigrationError::DeserializationError(_))
3157 ));
3158 }
3159
3160 #[test]
3161 fn test_load_from_non_object() {
3162 let path = Migrator::define("test").from::<V3>().into::<Domain>();
3163
3164 let mut migrator = Migrator::new();
3165 migrator.register(path).unwrap();
3166
3167 let json_value: serde_json::Value = serde_json::json!("just a string");
3169
3170 let result: Result<Domain, MigrationError> = migrator.load_from("test", json_value);
3171 assert!(matches!(
3172 result,
3173 Err(MigrationError::DeserializationError(_))
3174 ));
3175 }
3176
3177 #[test]
3178 fn test_load_from_invalid_domain_conversion() {
3179 #[derive(Serialize, Deserialize, Debug)]
3180 struct StrictDomain {
3181 required_field: String,
3182 }
3183
3184 impl IntoDomain<StrictDomain> for V3 {
3185 fn into_domain(self) -> StrictDomain {
3186 StrictDomain {
3187 required_field: self.value,
3188 }
3189 }
3190 }
3191
3192 let path = Migrator::define("strict")
3193 .from::<V3>()
3194 .into::<StrictDomain>();
3195
3196 let mut migrator = Migrator::new();
3197 migrator.register(path).unwrap();
3198
3199 let json = r#"{"version":"3.0.0","data":{"value":"test","count":1,"enabled":true}}"#;
3202
3203 let result: Result<StrictDomain, MigrationError> = migrator.load("strict", json);
3205 assert!(result.is_ok());
3206 assert_eq!(result.unwrap().required_field, "test");
3207 }
3208
3209 #[test]
3210 fn test_save_flat_and_load_flat() {
3211 let path = Migrator::define("test").from::<V3>().into::<Domain>();
3212
3213 let mut migrator = Migrator::new();
3214 migrator.register(path).unwrap();
3215
3216 let v3 = V3 {
3217 value: "flat_test".to_string(),
3218 count: 99,
3219 enabled: true,
3220 };
3221
3222 let json = migrator.save_flat(v3).unwrap();
3223
3224 assert!(json.contains("\"version\":\"3.0.0\""));
3226 assert!(json.contains("\"value\":\"flat_test\""));
3227 assert!(!json.contains("\"data\":"));
3228
3229 let result: Domain = migrator.load_flat("test", &json).unwrap();
3231 assert_eq!(result.value, "flat_test");
3232 assert_eq!(result.count, 99);
3233 assert!(result.enabled);
3234 }
3235
3236 #[test]
3237 fn test_load_from_with_fallback_version_not_string() {
3238 let path = Migrator::define("test")
3239 .from::<V1>()
3240 .step::<V2>()
3241 .step::<V3>()
3242 .into::<Domain>();
3243
3244 let mut migrator = Migrator::new();
3245 migrator.register(path).unwrap();
3246
3247 let json_value: serde_json::Value = serde_json::json!({
3249 "version": 100,
3250 "value": "fallback_test"
3251 });
3252
3253 let result: Domain = migrator
3254 .load_from_with_fallback("test", json_value)
3255 .unwrap();
3256 assert_eq!(result.value, "fallback_test");
3257 assert_eq!(result.count, 0); assert!(result.enabled); }
3260
3261 #[test]
3262 fn test_load_from_with_fallback_missing_data_field() {
3263 let path = Migrator::define("test")
3264 .from::<V1>()
3265 .step::<V2>()
3266 .step::<V3>()
3267 .into::<Domain>();
3268
3269 let mut migrator = Migrator::new();
3270 migrator.register(path).unwrap();
3271
3272 let json_value: serde_json::Value = serde_json::json!({
3274 "version": "1.0.0"
3275 });
3276
3277 let result: Result<Domain, MigrationError> =
3278 migrator.load_from_with_fallback("test", json_value);
3279 assert!(result.is_err());
3280 let err = result.unwrap_err();
3281 assert!(
3282 matches!(err, MigrationError::DeserializationError(ref msg) if msg.contains("data")),
3283 "Expected DeserializationError about missing data field, got: {:?}",
3284 err
3285 );
3286 }
3287
3288 #[test]
3289 fn test_register_second_version_invalid_semver() {
3290 #[derive(Serialize, Deserialize, Debug)]
3292 struct BadV2 {
3293 value: String,
3294 count: u32,
3295 }
3296
3297 impl Versioned for BadV2 {
3298 const VERSION: &'static str = "not-a-version"; }
3300
3301 impl MigratesTo<BadV2> for V1 {
3302 fn migrate(self) -> BadV2 {
3303 BadV2 {
3304 value: self.value,
3305 count: 0,
3306 }
3307 }
3308 }
3309
3310 impl IntoDomain<Domain> for BadV2 {
3311 fn into_domain(self) -> Domain {
3312 Domain {
3313 value: self.value,
3314 count: self.count,
3315 enabled: false,
3316 }
3317 }
3318 }
3319
3320 let path = Migrator::define("test")
3321 .from::<V1>()
3322 .step::<BadV2>()
3323 .into::<Domain>();
3324
3325 let mut migrator = Migrator::new();
3326 let result = migrator.register(path);
3327
3328 assert!(result.is_err());
3329 let err = result.unwrap_err();
3330 assert!(
3331 matches!(err, MigrationError::DeserializationError(ref msg) if msg.contains("not-a-version")),
3332 "Expected error about invalid semver 'not-a-version', got: {:?}",
3333 err
3334 );
3335 }
3336
3337 #[test]
3338 fn test_load_from_with_fallback_non_object() {
3339 let path = Migrator::define("test").from::<V3>().into::<Domain>();
3340
3341 let mut migrator = Migrator::new();
3342 migrator.register(path).unwrap();
3343
3344 let json_value: serde_json::Value = serde_json::json!([1, 2, 3]);
3346
3347 let result: Result<Domain, MigrationError> =
3348 migrator.load_from_with_fallback("test", json_value);
3349 assert!(result.is_err());
3350 let err = result.unwrap_err();
3351 assert!(
3352 matches!(err, MigrationError::DeserializationError(ref msg) if msg.contains("object")),
3353 "Expected DeserializationError about object format, got: {:?}",
3354 err
3355 );
3356 }
3357}