1use crate::errors::MigrationError;
4use crate::{IntoDomain, MigratesTo, Versioned};
5use serde::de::DeserializeOwned;
6use serde::Serialize;
7use std::collections::HashMap;
8use std::marker::PhantomData;
9
10type MigrationFn =
11 Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync>;
12
13type DomainSaveFn =
15 Box<dyn Fn(serde_json::Value, &str, &str) -> Result<String, MigrationError> + Send + Sync>;
16type DomainSaveFlatFn =
17 Box<dyn Fn(serde_json::Value, &str) -> Result<String, MigrationError> + Send + Sync>;
18
19struct EntityMigrationPath {
21 steps: HashMap<String, MigrationFn>,
23 finalize:
25 Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync>,
26 versions: Vec<String>,
28 version_key: String,
30 data_key: String,
32}
33
34struct DomainSavers {
36 save_fn: DomainSaveFn,
37 save_flat_fn: DomainSaveFlatFn,
38}
39
40pub struct Migrator {
42 paths: HashMap<String, EntityMigrationPath>,
43 default_version_key: Option<String>,
44 default_data_key: Option<String>,
45 domain_savers: HashMap<String, DomainSavers>,
46}
47
48impl Migrator {
49 pub fn new() -> Self {
51 Self {
52 paths: HashMap::new(),
53 default_version_key: None,
54 default_data_key: None,
55 domain_savers: HashMap::new(),
56 }
57 }
58
59 pub fn get_latest_version(&self, entity: &str) -> Option<&str> {
65 self.paths
66 .get(entity)
67 .and_then(|path| path.versions.last())
68 .map(|v| v.as_str())
69 }
70
71 pub fn builder() -> MigratorBuilder {
82 MigratorBuilder::new()
83 }
84
85 pub fn define(entity: &str) -> MigrationPathBuilder<Start> {
87 MigrationPathBuilder::new(entity.to_string())
88 }
89
90 pub fn register<D>(&mut self, path: MigrationPath<D>) -> Result<(), MigrationError> {
100 Self::validate_migration_path(&path.entity, &path.versions)?;
101
102 let version_key = path
104 .custom_version_key
105 .or_else(|| self.default_version_key.clone())
106 .unwrap_or_else(|| path.inner.version_key.clone());
107
108 let data_key = path
109 .custom_data_key
110 .or_else(|| self.default_data_key.clone())
111 .unwrap_or_else(|| path.inner.data_key.clone());
112
113 let entity_name = path.entity.clone();
114 let final_path = EntityMigrationPath {
115 steps: path.inner.steps,
116 finalize: path.inner.finalize,
117 versions: path.versions,
118 version_key,
119 data_key,
120 };
121
122 self.paths.insert(path.entity, final_path);
123
124 if let (Some(save_fn), Some(save_flat_fn)) = (path.save_fn, path.save_flat_fn) {
126 self.domain_savers.insert(
127 entity_name,
128 DomainSavers {
129 save_fn,
130 save_flat_fn,
131 },
132 );
133 }
134
135 Ok(())
136 }
137
138 fn validate_migration_path(entity: &str, versions: &[String]) -> Result<(), MigrationError> {
140 Self::check_circular_path(entity, versions)?;
142
143 Self::check_version_ordering(entity, versions)?;
145
146 Ok(())
147 }
148
149 fn check_circular_path(entity: &str, versions: &[String]) -> Result<(), MigrationError> {
151 let mut seen = std::collections::HashSet::new();
152
153 for version in versions {
154 if !seen.insert(version) {
155 let path = versions.join(" -> ");
157 return Err(MigrationError::CircularMigrationPath {
158 entity: entity.to_string(),
159 path,
160 });
161 }
162 }
163
164 Ok(())
165 }
166
167 fn check_version_ordering(entity: &str, versions: &[String]) -> Result<(), MigrationError> {
169 for i in 0..versions.len().saturating_sub(1) {
170 let current = &versions[i];
171 let next = &versions[i + 1];
172
173 let current_ver = semver::Version::parse(current).map_err(|e| {
175 MigrationError::DeserializationError(format!("Invalid semver '{}': {}", current, e))
176 })?;
177
178 let next_ver = semver::Version::parse(next).map_err(|e| {
179 MigrationError::DeserializationError(format!("Invalid semver '{}': {}", next, e))
180 })?;
181
182 if next_ver <= current_ver {
184 return Err(MigrationError::InvalidVersionOrder {
185 entity: entity.to_string(),
186 from: current.clone(),
187 to: next.clone(),
188 });
189 }
190 }
191
192 Ok(())
193 }
194
195 pub fn load_from<D, T>(&self, entity: &str, data: T) -> Result<D, MigrationError>
228 where
229 D: DeserializeOwned,
230 T: Serialize,
231 {
232 let value = serde_json::to_value(data).map_err(|e| {
234 MigrationError::DeserializationError(format!(
235 "Failed to convert input data to internal format: {}",
236 e
237 ))
238 })?;
239
240 let path = self
242 .paths
243 .get(entity)
244 .ok_or_else(|| MigrationError::EntityNotFound(entity.to_string()))?;
245
246 let version_key = &path.version_key;
247 let data_key = &path.data_key;
248
249 let obj = value.as_object().ok_or_else(|| {
251 MigrationError::DeserializationError(
252 "Expected object with version and data fields".to_string(),
253 )
254 })?;
255
256 let current_version = obj
257 .get(version_key)
258 .and_then(|v| v.as_str())
259 .ok_or_else(|| {
260 MigrationError::DeserializationError(format!(
261 "Missing or invalid '{}' field",
262 version_key
263 ))
264 })?
265 .to_string();
266
267 let mut current_data = obj
268 .get(data_key)
269 .ok_or_else(|| {
270 MigrationError::DeserializationError(format!("Missing '{}' field", data_key))
271 })?
272 .clone();
273
274 let mut current_version = current_version;
275
276 while let Some(migrate_fn) = path.steps.get(¤t_version) {
278 current_data = migrate_fn(current_data.clone())?;
280
281 match path.versions.iter().position(|v| v == ¤t_version) {
284 Some(idx) if idx + 1 < path.versions.len() => {
285 current_version = path.versions[idx + 1].clone();
286 }
287 _ => break,
288 }
289 }
290
291 let domain_value = (path.finalize)(current_data)?;
293
294 serde_json::from_value(domain_value).map_err(|e| {
295 MigrationError::DeserializationError(format!("Failed to convert to domain: {}", e))
296 })
297 }
298
299 pub fn load<D: DeserializeOwned>(&self, entity: &str, json: &str) -> Result<D, MigrationError> {
327 let data: serde_json::Value = serde_json::from_str(json).map_err(|e| {
328 MigrationError::DeserializationError(format!("Failed to parse JSON: {}", e))
329 })?;
330 self.load_from(entity, data)
331 }
332
333 pub fn load_from_with_fallback<D, T>(&self, entity: &str, data: T) -> Result<D, MigrationError>
366 where
367 D: DeserializeOwned,
368 T: Serialize,
369 {
370 let value = serde_json::to_value(data).map_err(|e| {
372 MigrationError::DeserializationError(format!(
373 "Failed to convert input data to internal format: {}",
374 e
375 ))
376 })?;
377
378 let path = self
380 .paths
381 .get(entity)
382 .ok_or_else(|| MigrationError::EntityNotFound(entity.to_string()))?;
383
384 let version_key = &path.version_key;
385 let data_key = &path.data_key;
386
387 let (current_version, current_data) = if let Some(obj) = value.as_object() {
389 if let Some(version_value) = obj.get(version_key) {
390 if let Some(version_str) = version_value.as_str() {
391 let data = obj
393 .get(data_key)
394 .ok_or_else(|| {
395 MigrationError::DeserializationError(format!(
396 "Missing '{}' field",
397 data_key
398 ))
399 })?
400 .clone();
401 (version_str.to_string(), data)
402 } else {
403 if path.versions.is_empty() {
405 return Err(MigrationError::DeserializationError(
406 "No migration versions defined for fallback".to_string(),
407 ));
408 }
409 (path.versions[0].clone(), value)
410 }
411 } else {
412 if path.versions.is_empty() {
414 return Err(MigrationError::DeserializationError(
415 "No migration versions defined for fallback".to_string(),
416 ));
417 }
418 (path.versions[0].clone(), value)
419 }
420 } else {
421 return Err(MigrationError::DeserializationError(
422 "Expected object format for versioned data".to_string(),
423 ));
424 };
425
426 let mut current_version = current_version;
427 let mut current_data = current_data;
428
429 while let Some(migrate_fn) = path.steps.get(¤t_version) {
431 current_data = migrate_fn(current_data.clone())?;
433
434 match path.versions.iter().position(|v| v == ¤t_version) {
437 Some(idx) if idx + 1 < path.versions.len() => {
438 current_version = path.versions[idx + 1].clone();
439 }
440 _ => break,
441 }
442 }
443
444 let domain_value = (path.finalize)(current_data)?;
446
447 serde_json::from_value(domain_value).map_err(|e| {
448 MigrationError::DeserializationError(format!("Failed to convert to domain: {}", e))
449 })
450 }
451
452 pub fn load_with_fallback<D: DeserializeOwned>(
484 &self,
485 entity: &str,
486 json: &str,
487 ) -> Result<D, MigrationError> {
488 let data: serde_json::Value = serde_json::from_str(json).map_err(|e| {
489 MigrationError::DeserializationError(format!("Failed to parse JSON: {}", e))
490 })?;
491 self.load_from_with_fallback(entity, data)
492 }
493
494 pub fn load_flat<D: DeserializeOwned>(
522 &self,
523 entity: &str,
524 json: &str,
525 ) -> Result<D, MigrationError> {
526 let data: serde_json::Value = serde_json::from_str(json).map_err(|e| {
527 MigrationError::DeserializationError(format!("Failed to parse JSON: {}", e))
528 })?;
529 self.load_flat_from(entity, data)
530 }
531
532 pub fn load_flat_from<D, T>(&self, entity: &str, value: T) -> Result<D, MigrationError>
561 where
562 D: DeserializeOwned,
563 T: Serialize,
564 {
565 let path = self
566 .paths
567 .get(entity)
568 .ok_or_else(|| MigrationError::EntityNotFound(entity.to_string()))?;
569
570 let version_key = &path.version_key;
571
572 let mut value = serde_json::to_value(value).map_err(|e| {
574 MigrationError::SerializationError(format!("Failed to convert input: {}", e))
575 })?;
576
577 let obj = value.as_object_mut().ok_or_else(|| {
579 MigrationError::DeserializationError(
580 "Expected object with version field at top level".to_string(),
581 )
582 })?;
583
584 let current_version = obj
585 .remove(version_key)
586 .ok_or_else(|| {
587 MigrationError::DeserializationError(format!(
588 "Missing '{}' field in flat format",
589 version_key
590 ))
591 })?
592 .as_str()
593 .ok_or_else(|| {
594 MigrationError::DeserializationError(format!(
595 "Invalid '{}' field type",
596 version_key
597 ))
598 })?
599 .to_string();
600
601 let mut current_data = serde_json::Value::Object(obj.clone());
603 let mut current_version = current_version;
604
605 while let Some(migrate_fn) = path.steps.get(¤t_version) {
607 current_data = migrate_fn(current_data.clone())?;
609
610 match path.versions.iter().position(|v| v == ¤t_version) {
612 Some(idx) if idx + 1 < path.versions.len() => {
613 current_version = path.versions[idx + 1].clone();
614 }
615 _ => break,
616 }
617 }
618
619 let domain_value = (path.finalize)(current_data)?;
621
622 serde_json::from_value(domain_value).map_err(|e| {
623 MigrationError::DeserializationError(format!("Failed to convert to domain: {}", e))
624 })
625 }
626
627 pub fn save<T: Versioned + Serialize>(&self, data: T) -> Result<String, MigrationError> {
658 let version_key = T::VERSION_KEY;
660 let data_key = T::DATA_KEY;
661
662 let data_value = serde_json::to_value(&data).map_err(|e| {
664 MigrationError::SerializationError(format!("Failed to serialize data: {}", e))
665 })?;
666
667 let mut map = serde_json::Map::new();
669 map.insert(
670 version_key.to_string(),
671 serde_json::Value::String(T::VERSION.to_string()),
672 );
673 map.insert(data_key.to_string(), data_value);
674
675 serde_json::to_string(&map).map_err(|e| {
676 MigrationError::SerializationError(format!("Failed to serialize wrapper: {}", e))
677 })
678 }
679
680 pub fn save_flat<T: Versioned + Serialize>(&self, data: T) -> Result<String, MigrationError> {
710 let version_key = T::VERSION_KEY;
711
712 let mut data_value = serde_json::to_value(&data).map_err(|e| {
714 MigrationError::SerializationError(format!("Failed to serialize data: {}", e))
715 })?;
716
717 let obj = data_value.as_object_mut().ok_or_else(|| {
719 MigrationError::SerializationError(
720 "Data must serialize to a JSON object for flat format".to_string(),
721 )
722 })?;
723
724 obj.insert(
726 version_key.to_string(),
727 serde_json::Value::String(T::VERSION.to_string()),
728 );
729
730 serde_json::to_string(&obj).map_err(|e| {
731 MigrationError::SerializationError(format!("Failed to serialize flat format: {}", e))
732 })
733 }
734
735 pub fn load_vec_from<D, T>(&self, entity: &str, data: Vec<T>) -> Result<Vec<D>, MigrationError>
768 where
769 D: DeserializeOwned,
770 T: Serialize,
771 {
772 data.into_iter()
773 .map(|item| self.load_from(entity, item))
774 .collect()
775 }
776
777 pub fn load_vec<D: DeserializeOwned>(
808 &self,
809 entity: &str,
810 json: &str,
811 ) -> Result<Vec<D>, MigrationError> {
812 let data: Vec<serde_json::Value> = serde_json::from_str(json).map_err(|e| {
813 MigrationError::DeserializationError(format!("Failed to parse JSON array: {}", e))
814 })?;
815 self.load_vec_from(entity, data)
816 }
817
818 pub fn load_vec_flat<D: DeserializeOwned>(
849 &self,
850 entity: &str,
851 json: &str,
852 ) -> Result<Vec<D>, MigrationError> {
853 let data: Vec<serde_json::Value> = serde_json::from_str(json).map_err(|e| {
854 MigrationError::DeserializationError(format!("Failed to parse JSON array: {}", e))
855 })?;
856 self.load_vec_flat_from(entity, data)
857 }
858
859 pub fn load_vec_flat_from<D, T>(
888 &self,
889 entity: &str,
890 data: Vec<T>,
891 ) -> Result<Vec<D>, MigrationError>
892 where
893 D: DeserializeOwned,
894 T: Serialize,
895 {
896 data.into_iter()
897 .map(|item| self.load_flat_from(entity, item))
898 .collect()
899 }
900
901 pub fn save_vec<T: Versioned + Serialize>(
938 &self,
939 data: Vec<T>,
940 ) -> Result<String, MigrationError> {
941 let version_key = T::VERSION_KEY;
942 let data_key = T::DATA_KEY;
943
944 let wrappers: Result<Vec<serde_json::Value>, MigrationError> = data
945 .into_iter()
946 .map(|item| {
947 let data_value = serde_json::to_value(&item).map_err(|e| {
948 MigrationError::SerializationError(format!("Failed to serialize item: {}", e))
949 })?;
950
951 let mut map = serde_json::Map::new();
952 map.insert(
953 version_key.to_string(),
954 serde_json::Value::String(T::VERSION.to_string()),
955 );
956 map.insert(data_key.to_string(), data_value);
957
958 Ok(serde_json::Value::Object(map))
959 })
960 .collect();
961
962 serde_json::to_string(&wrappers?).map_err(|e| {
963 MigrationError::SerializationError(format!("Failed to serialize data array: {}", e))
964 })
965 }
966
967 pub fn save_vec_flat<T: Versioned + Serialize>(
1003 &self,
1004 data: Vec<T>,
1005 ) -> Result<String, MigrationError> {
1006 let version_key = T::VERSION_KEY;
1007
1008 let flat_items: Result<Vec<serde_json::Value>, MigrationError> = data
1009 .into_iter()
1010 .map(|item| {
1011 let mut data_value = serde_json::to_value(&item).map_err(|e| {
1012 MigrationError::SerializationError(format!("Failed to serialize item: {}", e))
1013 })?;
1014
1015 let obj = data_value.as_object_mut().ok_or_else(|| {
1016 MigrationError::SerializationError(
1017 "Data must serialize to a JSON object for flat format".to_string(),
1018 )
1019 })?;
1020
1021 obj.insert(
1023 version_key.to_string(),
1024 serde_json::Value::String(T::VERSION.to_string()),
1025 );
1026
1027 Ok(serde_json::Value::Object(obj.clone()))
1028 })
1029 .collect();
1030
1031 serde_json::to_string(&flat_items?).map_err(|e| {
1032 MigrationError::SerializationError(format!(
1033 "Failed to serialize flat data array: {}",
1034 e
1035 ))
1036 })
1037 }
1038
1039 pub fn save_entity<E: crate::LatestVersioned>(
1077 &self,
1078 entity: E,
1079 ) -> Result<String, MigrationError> {
1080 let latest = entity.to_latest();
1081 self.save(latest)
1082 }
1083
1084 pub fn save_entity_flat<E: crate::LatestVersioned>(
1122 &self,
1123 entity: E,
1124 ) -> Result<String, MigrationError> {
1125 let latest = entity.to_latest();
1126 self.save_flat(latest)
1127 }
1128
1129 pub fn save_entity_vec<E: crate::LatestVersioned>(
1157 &self,
1158 entities: Vec<E>,
1159 ) -> Result<String, MigrationError> {
1160 let versioned: Vec<E::Latest> = entities.into_iter().map(|e| e.to_latest()).collect();
1161 self.save_vec(versioned)
1162 }
1163
1164 pub fn save_entity_vec_flat<E: crate::LatestVersioned>(
1192 &self,
1193 entities: Vec<E>,
1194 ) -> Result<String, MigrationError> {
1195 let versioned: Vec<E::Latest> = entities.into_iter().map(|e| e.to_latest()).collect();
1196 self.save_vec_flat(versioned)
1197 }
1198
1199 pub fn save_domain<T: Serialize>(
1237 &self,
1238 entity_name: &str,
1239 entity: T,
1240 ) -> Result<String, MigrationError> {
1241 let saver = self.domain_savers.get(entity_name).ok_or_else(|| {
1242 MigrationError::EntityNotFound(format!(
1243 "Entity '{}' is not registered with domain save support. Use into_with_save() when defining the migration path.",
1244 entity_name
1245 ))
1246 })?;
1247
1248 let path = self.paths.get(entity_name).ok_or_else(|| {
1250 MigrationError::EntityNotFound(format!("Entity '{}' is not registered", entity_name))
1251 })?;
1252
1253 let domain_value = serde_json::to_value(entity).map_err(|e| {
1254 MigrationError::SerializationError(format!("Failed to serialize entity: {}", e))
1255 })?;
1256
1257 (saver.save_fn)(domain_value, &path.version_key, &path.data_key)
1258 }
1259
1260 pub fn save_domain_flat<T: Serialize>(
1286 &self,
1287 entity_name: &str,
1288 entity: T,
1289 ) -> Result<String, MigrationError> {
1290 let saver = self.domain_savers.get(entity_name).ok_or_else(|| {
1291 MigrationError::EntityNotFound(format!(
1292 "Entity '{}' is not registered with domain save support. Use into_with_save() when defining the migration path.",
1293 entity_name
1294 ))
1295 })?;
1296
1297 let path = self.paths.get(entity_name).ok_or_else(|| {
1299 MigrationError::EntityNotFound(format!("Entity '{}' is not registered", entity_name))
1300 })?;
1301
1302 let domain_value = serde_json::to_value(entity).map_err(|e| {
1303 MigrationError::SerializationError(format!("Failed to serialize entity: {}", e))
1304 })?;
1305
1306 (saver.save_flat_fn)(domain_value, &path.version_key)
1307 }
1308}
1309
1310impl Default for Migrator {
1311 fn default() -> Self {
1312 Self::new()
1313 }
1314}
1315
1316pub struct MigratorBuilder {
1318 default_version_key: Option<String>,
1319 default_data_key: Option<String>,
1320}
1321
1322impl MigratorBuilder {
1323 pub(crate) fn new() -> Self {
1324 Self {
1325 default_version_key: None,
1326 default_data_key: None,
1327 }
1328 }
1329
1330 pub fn default_version_key(mut self, key: impl Into<String>) -> Self {
1336 self.default_version_key = Some(key.into());
1337 self
1338 }
1339
1340 pub fn default_data_key(mut self, key: impl Into<String>) -> Self {
1346 self.default_data_key = Some(key.into());
1347 self
1348 }
1349
1350 pub fn build(self) -> Migrator {
1352 Migrator {
1353 paths: HashMap::new(),
1354 default_version_key: self.default_version_key,
1355 default_data_key: self.default_data_key,
1356 domain_savers: HashMap::new(),
1357 }
1358 }
1359}
1360
1361pub struct Start;
1363
1364pub struct HasFrom<V>(PhantomData<V>);
1366
1367pub struct HasSteps<V>(PhantomData<V>);
1369
1370pub struct MigrationPathBuilder<State> {
1372 entity: String,
1373 steps: HashMap<String, MigrationFn>,
1374 versions: Vec<String>,
1375 version_key: String,
1376 data_key: String,
1377 custom_version_key: Option<String>,
1378 custom_data_key: Option<String>,
1379 _state: PhantomData<State>,
1380}
1381
1382impl MigrationPathBuilder<Start> {
1383 fn new(entity: String) -> Self {
1384 Self {
1385 entity,
1386 steps: HashMap::new(),
1387 versions: Vec::new(),
1388 version_key: String::from("version"),
1389 data_key: String::from("data"),
1390 custom_version_key: None,
1391 custom_data_key: None,
1392 _state: PhantomData,
1393 }
1394 }
1395
1396 pub fn with_keys(
1409 mut self,
1410 version_key: impl Into<String>,
1411 data_key: impl Into<String>,
1412 ) -> Self {
1413 self.custom_version_key = Some(version_key.into());
1414 self.custom_data_key = Some(data_key.into());
1415 self
1416 }
1417
1418 pub fn from<V: Versioned + DeserializeOwned>(self) -> MigrationPathBuilder<HasFrom<V>> {
1420 let mut versions = self.versions;
1421 versions.push(V::VERSION.to_string());
1422
1423 MigrationPathBuilder {
1424 entity: self.entity,
1425 steps: self.steps,
1426 versions,
1427 version_key: V::VERSION_KEY.to_string(),
1428 data_key: V::DATA_KEY.to_string(),
1429 custom_version_key: self.custom_version_key,
1430 custom_data_key: self.custom_data_key,
1431 _state: PhantomData,
1432 }
1433 }
1434}
1435
1436impl<V> MigrationPathBuilder<HasFrom<V>>
1437where
1438 V: Versioned + DeserializeOwned,
1439{
1440 pub fn step<Next>(mut self) -> MigrationPathBuilder<HasSteps<Next>>
1442 where
1443 V: MigratesTo<Next>,
1444 Next: Versioned + DeserializeOwned + Serialize,
1445 {
1446 let from_version = V::VERSION.to_string();
1447 let migration_fn: MigrationFn = Box::new(move |value| {
1448 let from_value: V = serde_json::from_value(value).map_err(|e| {
1449 MigrationError::DeserializationError(format!(
1450 "Failed to deserialize version {}: {}",
1451 V::VERSION,
1452 e
1453 ))
1454 })?;
1455
1456 let to_value = from_value.migrate();
1457
1458 serde_json::to_value(&to_value).map_err(|e| MigrationError::MigrationStepFailed {
1460 from: V::VERSION.to_string(),
1461 to: Next::VERSION.to_string(),
1462 error: e.to_string(),
1463 })
1464 });
1465
1466 self.steps.insert(from_version, migration_fn);
1467 self.versions.push(Next::VERSION.to_string());
1468
1469 MigrationPathBuilder {
1470 entity: self.entity,
1471 steps: self.steps,
1472 versions: self.versions,
1473 version_key: self.version_key,
1474 data_key: self.data_key,
1475 custom_version_key: self.custom_version_key,
1476 custom_data_key: self.custom_data_key,
1477 _state: PhantomData,
1478 }
1479 }
1480
1481 pub fn into<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1483 where
1484 V: IntoDomain<D>,
1485 {
1486 let finalize: Box<
1487 dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync,
1488 > = Box::new(move |value| {
1489 let versioned: V = serde_json::from_value(value).map_err(|e| {
1490 MigrationError::DeserializationError(format!(
1491 "Failed to deserialize final version: {}",
1492 e
1493 ))
1494 })?;
1495
1496 let domain = versioned.into_domain();
1497
1498 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1499 from: V::VERSION.to_string(),
1500 to: "domain".to_string(),
1501 error: e.to_string(),
1502 })
1503 });
1504
1505 MigrationPath {
1506 entity: self.entity,
1507 inner: EntityMigrationPath {
1508 steps: self.steps,
1509 finalize,
1510 versions: self.versions.clone(),
1511 version_key: self.version_key,
1512 data_key: self.data_key,
1513 },
1514 versions: self.versions,
1515 custom_version_key: self.custom_version_key,
1516 custom_data_key: self.custom_data_key,
1517 save_fn: None,
1518 save_flat_fn: None,
1519 _phantom: PhantomData,
1520 }
1521 }
1522
1523 pub fn into_with_save<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1558 where
1559 V: IntoDomain<D> + crate::FromDomain<D>,
1560 {
1561 let finalize: Box<
1562 dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync,
1563 > = Box::new(move |value| {
1564 let versioned: V = serde_json::from_value(value).map_err(|e| {
1565 MigrationError::DeserializationError(format!(
1566 "Failed to deserialize final version: {}",
1567 e
1568 ))
1569 })?;
1570
1571 let domain = versioned.into_domain();
1572
1573 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1574 from: V::VERSION.to_string(),
1575 to: "domain".to_string(),
1576 error: e.to_string(),
1577 })
1578 });
1579
1580 let version = V::VERSION;
1582
1583 let save_fn: DomainSaveFn = Box::new(move |domain_value, vkey, dkey| {
1584 let domain: D = serde_json::from_value(domain_value).map_err(|e| {
1585 MigrationError::DeserializationError(format!("Failed to deserialize domain: {}", e))
1586 })?;
1587
1588 let latest = V::from_domain(domain);
1589 let data_value = serde_json::to_value(&latest).map_err(|e| {
1590 MigrationError::SerializationError(format!("Failed to serialize latest: {}", e))
1591 })?;
1592
1593 let mut map = serde_json::Map::new();
1594 map.insert(
1595 vkey.to_string(),
1596 serde_json::Value::String(version.to_string()),
1597 );
1598 map.insert(dkey.to_string(), data_value);
1599
1600 serde_json::to_string(&map).map_err(|e| {
1601 MigrationError::SerializationError(format!("Failed to serialize wrapper: {}", e))
1602 })
1603 });
1604
1605 let save_flat_fn: DomainSaveFlatFn = Box::new(move |domain_value, vkey| {
1606 let domain: D = serde_json::from_value(domain_value).map_err(|e| {
1607 MigrationError::DeserializationError(format!("Failed to deserialize domain: {}", e))
1608 })?;
1609
1610 let latest = V::from_domain(domain);
1611 let mut data_value = serde_json::to_value(&latest).map_err(|e| {
1612 MigrationError::SerializationError(format!("Failed to serialize latest: {}", e))
1613 })?;
1614
1615 let obj = data_value.as_object_mut().ok_or_else(|| {
1616 MigrationError::SerializationError(
1617 "Data must serialize to a JSON object for flat format".to_string(),
1618 )
1619 })?;
1620
1621 obj.insert(
1622 vkey.to_string(),
1623 serde_json::Value::String(version.to_string()),
1624 );
1625
1626 serde_json::to_string(&obj).map_err(|e| {
1627 MigrationError::SerializationError(format!(
1628 "Failed to serialize flat format: {}",
1629 e
1630 ))
1631 })
1632 });
1633
1634 MigrationPath {
1635 entity: self.entity,
1636 inner: EntityMigrationPath {
1637 steps: self.steps,
1638 finalize,
1639 versions: self.versions.clone(),
1640 version_key: self.version_key,
1641 data_key: self.data_key,
1642 },
1643 versions: self.versions,
1644 custom_version_key: self.custom_version_key,
1645 custom_data_key: self.custom_data_key,
1646 save_fn: Some(save_fn),
1647 save_flat_fn: Some(save_flat_fn),
1648 _phantom: PhantomData,
1649 }
1650 }
1651}
1652
1653impl<V> MigrationPathBuilder<HasSteps<V>>
1654where
1655 V: Versioned + DeserializeOwned,
1656{
1657 pub fn step<Next>(mut self) -> MigrationPathBuilder<HasSteps<Next>>
1659 where
1660 V: MigratesTo<Next>,
1661 Next: Versioned + DeserializeOwned + Serialize,
1662 {
1663 let from_version = V::VERSION.to_string();
1664 let migration_fn: MigrationFn = Box::new(move |value| {
1665 let from_value: V = serde_json::from_value(value).map_err(|e| {
1666 MigrationError::DeserializationError(format!(
1667 "Failed to deserialize version {}: {}",
1668 V::VERSION,
1669 e
1670 ))
1671 })?;
1672
1673 let to_value = from_value.migrate();
1674
1675 serde_json::to_value(&to_value).map_err(|e| MigrationError::MigrationStepFailed {
1677 from: V::VERSION.to_string(),
1678 to: Next::VERSION.to_string(),
1679 error: e.to_string(),
1680 })
1681 });
1682
1683 self.steps.insert(from_version, migration_fn);
1684 self.versions.push(Next::VERSION.to_string());
1685
1686 MigrationPathBuilder {
1687 entity: self.entity,
1688 steps: self.steps,
1689 versions: self.versions,
1690 version_key: self.version_key,
1691 data_key: self.data_key,
1692 custom_version_key: self.custom_version_key,
1693 custom_data_key: self.custom_data_key,
1694 _state: PhantomData,
1695 }
1696 }
1697
1698 pub fn into<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1700 where
1701 V: IntoDomain<D>,
1702 {
1703 let finalize: Box<
1704 dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync,
1705 > = Box::new(move |value| {
1706 let versioned: V = serde_json::from_value(value).map_err(|e| {
1707 MigrationError::DeserializationError(format!(
1708 "Failed to deserialize final version: {}",
1709 e
1710 ))
1711 })?;
1712
1713 let domain = versioned.into_domain();
1714
1715 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1716 from: V::VERSION.to_string(),
1717 to: "domain".to_string(),
1718 error: e.to_string(),
1719 })
1720 });
1721
1722 MigrationPath {
1723 entity: self.entity,
1724 inner: EntityMigrationPath {
1725 steps: self.steps,
1726 finalize,
1727 versions: self.versions.clone(),
1728 version_key: self.version_key,
1729 data_key: self.data_key,
1730 },
1731 versions: self.versions,
1732 custom_version_key: self.custom_version_key,
1733 custom_data_key: self.custom_data_key,
1734 save_fn: None,
1735 save_flat_fn: None,
1736 _phantom: PhantomData,
1737 }
1738 }
1739
1740 pub fn into_with_save<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1744 where
1745 V: IntoDomain<D> + crate::FromDomain<D>,
1746 {
1747 let finalize: Box<
1748 dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError> + Send + Sync,
1749 > = Box::new(move |value| {
1750 let versioned: V = serde_json::from_value(value).map_err(|e| {
1751 MigrationError::DeserializationError(format!(
1752 "Failed to deserialize final version: {}",
1753 e
1754 ))
1755 })?;
1756
1757 let domain = versioned.into_domain();
1758
1759 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1760 from: V::VERSION.to_string(),
1761 to: "domain".to_string(),
1762 error: e.to_string(),
1763 })
1764 });
1765
1766 let version = V::VERSION;
1768
1769 let save_fn: DomainSaveFn = Box::new(move |domain_value, vkey, dkey| {
1770 let domain: D = serde_json::from_value(domain_value).map_err(|e| {
1771 MigrationError::DeserializationError(format!("Failed to deserialize domain: {}", e))
1772 })?;
1773
1774 let latest = V::from_domain(domain);
1775 let data_value = serde_json::to_value(&latest).map_err(|e| {
1776 MigrationError::SerializationError(format!("Failed to serialize latest: {}", e))
1777 })?;
1778
1779 let mut map = serde_json::Map::new();
1780 map.insert(
1781 vkey.to_string(),
1782 serde_json::Value::String(version.to_string()),
1783 );
1784 map.insert(dkey.to_string(), data_value);
1785
1786 serde_json::to_string(&map).map_err(|e| {
1787 MigrationError::SerializationError(format!("Failed to serialize wrapper: {}", e))
1788 })
1789 });
1790
1791 let save_flat_fn: DomainSaveFlatFn = Box::new(move |domain_value, vkey| {
1792 let domain: D = serde_json::from_value(domain_value).map_err(|e| {
1793 MigrationError::DeserializationError(format!("Failed to deserialize domain: {}", e))
1794 })?;
1795
1796 let latest = V::from_domain(domain);
1797 let mut data_value = serde_json::to_value(&latest).map_err(|e| {
1798 MigrationError::SerializationError(format!("Failed to serialize latest: {}", e))
1799 })?;
1800
1801 let obj = data_value.as_object_mut().ok_or_else(|| {
1802 MigrationError::SerializationError(
1803 "Data must serialize to a JSON object for flat format".to_string(),
1804 )
1805 })?;
1806
1807 obj.insert(
1808 vkey.to_string(),
1809 serde_json::Value::String(version.to_string()),
1810 );
1811
1812 serde_json::to_string(&obj).map_err(|e| {
1813 MigrationError::SerializationError(format!(
1814 "Failed to serialize flat format: {}",
1815 e
1816 ))
1817 })
1818 });
1819
1820 MigrationPath {
1821 entity: self.entity,
1822 inner: EntityMigrationPath {
1823 steps: self.steps,
1824 finalize,
1825 versions: self.versions.clone(),
1826 version_key: self.version_key,
1827 data_key: self.data_key,
1828 },
1829 versions: self.versions,
1830 custom_version_key: self.custom_version_key,
1831 custom_data_key: self.custom_data_key,
1832 save_fn: Some(save_fn),
1833 save_flat_fn: Some(save_flat_fn),
1834 _phantom: PhantomData,
1835 }
1836 }
1837}
1838
1839pub struct MigrationPath<D> {
1841 entity: String,
1842 inner: EntityMigrationPath,
1843 versions: Vec<String>,
1845 custom_version_key: Option<String>,
1847 custom_data_key: Option<String>,
1849 save_fn: Option<DomainSaveFn>,
1851 save_flat_fn: Option<DomainSaveFlatFn>,
1853 _phantom: PhantomData<D>,
1854}
1855
1856pub struct ConfigMigrator {
1891 root: serde_json::Value,
1892 migrator: Migrator,
1893}
1894
1895impl ConfigMigrator {
1896 pub fn from(json: &str, migrator: Migrator) -> Result<Self, MigrationError> {
1902 let root = serde_json::from_str(json)
1903 .map_err(|e| MigrationError::DeserializationError(e.to_string()))?;
1904 Ok(Self { root, migrator })
1905 }
1906
1907 pub fn query<T>(&self, key: &str) -> Result<Vec<T>, MigrationError>
1927 where
1928 T: crate::Queryable + for<'de> serde::Deserialize<'de>,
1929 {
1930 let value = &self.root[key];
1931 if value.is_null() {
1932 return Ok(Vec::new());
1933 }
1934
1935 if !value.is_array() {
1936 return Err(MigrationError::DeserializationError(format!(
1937 "Key '{}' does not contain an array",
1938 key
1939 )));
1940 }
1941
1942 let array = value.as_array().unwrap(); self.migrator
1944 .load_vec_flat_from(T::ENTITY_NAME, array.to_vec())
1945 }
1946
1947 pub fn update<T>(&mut self, key: &str, data: Vec<T>) -> Result<(), MigrationError>
1968 where
1969 T: serde::Serialize + crate::Queryable,
1970 {
1971 let entity_name = T::ENTITY_NAME;
1972 let latest_version = self
1973 .migrator
1974 .get_latest_version(entity_name)
1975 .ok_or_else(|| MigrationError::EntityNotFound(entity_name.to_string()))?;
1976
1977 let items: Vec<serde_json::Value> = data
1979 .into_iter()
1980 .map(|item| {
1981 let mut obj = serde_json::to_value(&item)
1982 .map_err(|e| MigrationError::SerializationError(e.to_string()))?;
1983
1984 if let Some(obj_map) = obj.as_object_mut() {
1985 obj_map.insert(
1986 "version".to_string(),
1987 serde_json::Value::String(latest_version.to_string()),
1988 );
1989 }
1990
1991 Ok(obj)
1992 })
1993 .collect::<Result<Vec<_>, MigrationError>>()?;
1994
1995 self.root[key] = serde_json::Value::Array(items);
1996 Ok(())
1997 }
1998
1999 pub fn to_string(&self) -> Result<String, MigrationError> {
2005 serde_json::to_string_pretty(&self.root)
2006 .map_err(|e| MigrationError::SerializationError(e.to_string()))
2007 }
2008
2009 pub fn to_string_compact(&self) -> Result<String, MigrationError> {
2015 serde_json::to_string(&self.root)
2016 .map_err(|e| MigrationError::SerializationError(e.to_string()))
2017 }
2018
2019 pub fn as_value(&self) -> &serde_json::Value {
2021 &self.root
2022 }
2023}
2024
2025#[cfg(test)]
2026mod tests {
2027 use super::*;
2028 use crate::{IntoDomain, MigratesTo, Versioned, VersionedWrapper};
2029 use serde::{Deserialize, Serialize};
2030
2031 #[derive(Serialize, Deserialize, Debug, PartialEq)]
2033 struct V1 {
2034 value: String,
2035 }
2036
2037 impl Versioned for V1 {
2038 const VERSION: &'static str = "1.0.0";
2039 }
2040
2041 #[derive(Serialize, Deserialize, Debug, PartialEq)]
2042 struct V2 {
2043 value: String,
2044 count: u32,
2045 }
2046
2047 impl Versioned for V2 {
2048 const VERSION: &'static str = "2.0.0";
2049 }
2050
2051 #[derive(Serialize, Deserialize, Debug, PartialEq)]
2052 struct V3 {
2053 value: String,
2054 count: u32,
2055 enabled: bool,
2056 }
2057
2058 impl Versioned for V3 {
2059 const VERSION: &'static str = "3.0.0";
2060 }
2061
2062 #[derive(Serialize, Deserialize, Debug, PartialEq)]
2063 struct Domain {
2064 value: String,
2065 count: u32,
2066 enabled: bool,
2067 }
2068
2069 impl MigratesTo<V2> for V1 {
2070 fn migrate(self) -> V2 {
2071 V2 {
2072 value: self.value,
2073 count: 0,
2074 }
2075 }
2076 }
2077
2078 impl MigratesTo<V3> for V2 {
2079 fn migrate(self) -> V3 {
2080 V3 {
2081 value: self.value,
2082 count: self.count,
2083 enabled: true,
2084 }
2085 }
2086 }
2087
2088 impl IntoDomain<Domain> for V3 {
2089 fn into_domain(self) -> Domain {
2090 Domain {
2091 value: self.value,
2092 count: self.count,
2093 enabled: self.enabled,
2094 }
2095 }
2096 }
2097
2098 #[test]
2099 fn test_migrator_new() {
2100 let migrator = Migrator::new();
2101 assert_eq!(migrator.paths.len(), 0);
2102 }
2103
2104 #[test]
2105 fn test_migrator_default() {
2106 let migrator = Migrator::default();
2107 assert_eq!(migrator.paths.len(), 0);
2108 }
2109
2110 #[test]
2111 fn test_single_step_migration() {
2112 let path = Migrator::define("test")
2113 .from::<V2>()
2114 .step::<V3>()
2115 .into::<Domain>();
2116
2117 let mut migrator = Migrator::new();
2118 migrator.register(path).unwrap();
2119
2120 let v2 = V2 {
2121 value: "test".to_string(),
2122 count: 42,
2123 };
2124 let wrapper = VersionedWrapper::from_versioned(v2);
2125 let json = serde_json::to_string(&wrapper).unwrap();
2126
2127 let result: Domain = migrator.load("test", &json).unwrap();
2128 assert_eq!(result.value, "test");
2129 assert_eq!(result.count, 42);
2130 assert!(result.enabled);
2131 }
2132
2133 #[test]
2134 fn test_multi_step_migration() {
2135 let path = Migrator::define("test")
2136 .from::<V1>()
2137 .step::<V2>()
2138 .step::<V3>()
2139 .into::<Domain>();
2140
2141 let mut migrator = Migrator::new();
2142 migrator.register(path).unwrap();
2143
2144 let v1 = V1 {
2145 value: "multi_step".to_string(),
2146 };
2147 let wrapper = VersionedWrapper::from_versioned(v1);
2148 let json = serde_json::to_string(&wrapper).unwrap();
2149
2150 let result: Domain = migrator.load("test", &json).unwrap();
2151 assert_eq!(result.value, "multi_step");
2152 assert_eq!(result.count, 0);
2153 assert!(result.enabled);
2154 }
2155
2156 #[test]
2157 fn test_no_migration_needed() {
2158 let path = Migrator::define("test").from::<V3>().into::<Domain>();
2159
2160 let mut migrator = Migrator::new();
2161 migrator.register(path).unwrap();
2162
2163 let v3 = V3 {
2164 value: "latest".to_string(),
2165 count: 100,
2166 enabled: false,
2167 };
2168 let wrapper = VersionedWrapper::from_versioned(v3);
2169 let json = serde_json::to_string(&wrapper).unwrap();
2170
2171 let result: Domain = migrator.load("test", &json).unwrap();
2172 assert_eq!(result.value, "latest");
2173 assert_eq!(result.count, 100);
2174 assert!(!result.enabled);
2175 }
2176
2177 #[test]
2178 fn test_entity_not_found() {
2179 let migrator = Migrator::new();
2180
2181 let v1 = V1 {
2182 value: "test".to_string(),
2183 };
2184 let wrapper = VersionedWrapper::from_versioned(v1);
2185 let json = serde_json::to_string(&wrapper).unwrap();
2186
2187 let result: Result<Domain, MigrationError> = migrator.load("unknown", &json);
2188 assert!(matches!(result, Err(MigrationError::EntityNotFound(_))));
2189
2190 if let Err(MigrationError::EntityNotFound(entity)) = result {
2191 assert_eq!(entity, "unknown");
2192 }
2193 }
2194
2195 #[test]
2196 fn test_invalid_json() {
2197 let path = Migrator::define("test").from::<V3>().into::<Domain>();
2198
2199 let mut migrator = Migrator::new();
2200 migrator.register(path).unwrap();
2201
2202 let invalid_json = "{ invalid json }";
2203 let result: Result<Domain, MigrationError> = migrator.load("test", invalid_json);
2204
2205 assert!(matches!(
2206 result,
2207 Err(MigrationError::DeserializationError(_))
2208 ));
2209 }
2210
2211 #[test]
2212 fn test_multiple_entities() {
2213 #[derive(Serialize, Deserialize, Debug, PartialEq)]
2214 struct OtherDomain {
2215 value: String,
2216 }
2217
2218 impl IntoDomain<OtherDomain> for V1 {
2219 fn into_domain(self) -> OtherDomain {
2220 OtherDomain { value: self.value }
2221 }
2222 }
2223
2224 let path1 = Migrator::define("entity1")
2225 .from::<V1>()
2226 .step::<V2>()
2227 .step::<V3>()
2228 .into::<Domain>();
2229
2230 let path2 = Migrator::define("entity2")
2231 .from::<V1>()
2232 .into::<OtherDomain>();
2233
2234 let mut migrator = Migrator::new();
2235 migrator.register(path1).unwrap();
2236 migrator.register(path2).unwrap();
2237
2238 let v1 = V1 {
2240 value: "entity1".to_string(),
2241 };
2242 let wrapper = VersionedWrapper::from_versioned(v1);
2243 let json = serde_json::to_string(&wrapper).unwrap();
2244 let result: Domain = migrator.load("entity1", &json).unwrap();
2245 assert_eq!(result.value, "entity1");
2246
2247 let v1 = V1 {
2249 value: "entity2".to_string(),
2250 };
2251 let wrapper = VersionedWrapper::from_versioned(v1);
2252 let json = serde_json::to_string(&wrapper).unwrap();
2253 let result: OtherDomain = migrator.load("entity2", &json).unwrap();
2254 assert_eq!(result.value, "entity2");
2255 }
2256
2257 #[test]
2258 fn test_save() {
2259 let migrator = Migrator::new();
2260
2261 let v1 = V1 {
2262 value: "test_save".to_string(),
2263 };
2264
2265 let json = migrator.save(v1).unwrap();
2266
2267 assert!(json.contains("\"version\""));
2269 assert!(json.contains("\"1.0.0\""));
2270 assert!(json.contains("\"data\""));
2271 assert!(json.contains("\"test_save\""));
2272
2273 let parsed: VersionedWrapper<serde_json::Value> = serde_json::from_str(&json).unwrap();
2275 assert_eq!(parsed.version, "1.0.0");
2276 }
2277
2278 #[test]
2279 fn test_save_and_load_roundtrip() {
2280 let path = Migrator::define("test")
2281 .from::<V1>()
2282 .step::<V2>()
2283 .step::<V3>()
2284 .into::<Domain>();
2285
2286 let mut migrator = Migrator::new();
2287 migrator.register(path).unwrap();
2288
2289 let v1 = V1 {
2291 value: "roundtrip".to_string(),
2292 };
2293 let json = migrator.save(v1).unwrap();
2294
2295 let domain: Domain = migrator.load("test", &json).unwrap();
2297
2298 assert_eq!(domain.value, "roundtrip");
2299 assert_eq!(domain.count, 0); assert!(domain.enabled); }
2302
2303 #[test]
2304 fn test_save_latest_version() {
2305 let migrator = Migrator::new();
2306
2307 let v3 = V3 {
2308 value: "latest".to_string(),
2309 count: 42,
2310 enabled: false,
2311 };
2312
2313 let json = migrator.save(v3).unwrap();
2314
2315 assert!(json.contains("\"version\":\"3.0.0\""));
2317 assert!(json.contains("\"value\":\"latest\""));
2318 assert!(json.contains("\"count\":42"));
2319 assert!(json.contains("\"enabled\":false"));
2320 }
2321
2322 #[test]
2323 fn test_save_pretty() {
2324 let migrator = Migrator::new();
2325
2326 let v2 = V2 {
2327 value: "pretty".to_string(),
2328 count: 10,
2329 };
2330
2331 let json = migrator.save(v2).unwrap();
2332
2333 assert!(!json.contains('\n'));
2335 assert!(json.contains("\"version\":\"2.0.0\""));
2336 }
2337
2338 #[test]
2339 fn test_validation_invalid_version_order() {
2340 let entity = "test".to_string();
2342 let versions = vec!["2.0.0".to_string(), "1.0.0".to_string()]; let result = Migrator::validate_migration_path(&entity, &versions);
2345 assert!(matches!(
2346 result,
2347 Err(MigrationError::InvalidVersionOrder { .. })
2348 ));
2349
2350 if let Err(MigrationError::InvalidVersionOrder {
2351 entity: e,
2352 from,
2353 to,
2354 }) = result
2355 {
2356 assert_eq!(e, "test");
2357 assert_eq!(from, "2.0.0");
2358 assert_eq!(to, "1.0.0");
2359 }
2360 }
2361
2362 #[test]
2363 fn test_validation_circular_path() {
2364 let entity = "test".to_string();
2366 let versions = vec![
2367 "1.0.0".to_string(),
2368 "2.0.0".to_string(),
2369 "1.0.0".to_string(), ];
2371
2372 let result = Migrator::validate_migration_path(&entity, &versions);
2373 assert!(matches!(
2374 result,
2375 Err(MigrationError::CircularMigrationPath { .. })
2376 ));
2377
2378 if let Err(MigrationError::CircularMigrationPath { entity: e, path }) = result {
2379 assert_eq!(e, "test");
2380 assert!(path.contains("1.0.0"));
2381 assert!(path.contains("2.0.0"));
2382 }
2383 }
2384
2385 #[test]
2386 fn test_validation_valid_path() {
2387 let entity = "test".to_string();
2389 let versions = vec![
2390 "1.0.0".to_string(),
2391 "1.1.0".to_string(),
2392 "2.0.0".to_string(),
2393 ];
2394
2395 let result = Migrator::validate_migration_path(&entity, &versions);
2396 assert!(result.is_ok());
2397 }
2398
2399 #[test]
2400 fn test_validation_empty_path() {
2401 let entity = "test".to_string();
2403 let versions = vec![];
2404
2405 let result = Migrator::validate_migration_path(&entity, &versions);
2406 assert!(result.is_ok());
2407 }
2408
2409 #[test]
2410 fn test_validation_single_version() {
2411 let entity = "test".to_string();
2413 let versions = vec!["1.0.0".to_string()];
2414
2415 let result = Migrator::validate_migration_path(&entity, &versions);
2416 assert!(result.is_ok());
2417 }
2418
2419 #[test]
2421 fn test_save_vec_and_load_vec() {
2422 let migrator = Migrator::new();
2423
2424 let items = vec![
2426 V1 {
2427 value: "item1".to_string(),
2428 },
2429 V1 {
2430 value: "item2".to_string(),
2431 },
2432 V1 {
2433 value: "item3".to_string(),
2434 },
2435 ];
2436
2437 let json = migrator.save_vec(items).unwrap();
2438
2439 assert!(json.starts_with('['));
2441 assert!(json.ends_with(']'));
2442 assert!(json.contains("\"version\":\"1.0.0\""));
2443 assert!(json.contains("item1"));
2444 assert!(json.contains("item2"));
2445 assert!(json.contains("item3"));
2446
2447 let path = Migrator::define("test")
2449 .from::<V1>()
2450 .step::<V2>()
2451 .step::<V3>()
2452 .into::<Domain>();
2453
2454 let mut migrator = Migrator::new();
2455 migrator.register(path).unwrap();
2456
2457 let domains: Vec<Domain> = migrator.load_vec("test", &json).unwrap();
2459
2460 assert_eq!(domains.len(), 3);
2461 assert_eq!(domains[0].value, "item1");
2462 assert_eq!(domains[1].value, "item2");
2463 assert_eq!(domains[2].value, "item3");
2464
2465 for domain in &domains {
2467 assert_eq!(domain.count, 0);
2468 assert!(domain.enabled);
2469 }
2470 }
2471
2472 #[test]
2473 fn test_load_vec_empty_array() {
2474 let path = Migrator::define("test")
2475 .from::<V1>()
2476 .step::<V2>()
2477 .step::<V3>()
2478 .into::<Domain>();
2479
2480 let mut migrator = Migrator::new();
2481 migrator.register(path).unwrap();
2482
2483 let json = "[]";
2484 let domains: Vec<Domain> = migrator.load_vec("test", json).unwrap();
2485
2486 assert_eq!(domains.len(), 0);
2487 }
2488
2489 #[test]
2490 fn test_load_vec_mixed_versions() {
2491 let path = Migrator::define("test")
2493 .from::<V1>()
2494 .step::<V2>()
2495 .step::<V3>()
2496 .into::<Domain>();
2497
2498 let mut migrator = Migrator::new();
2499 migrator.register(path).unwrap();
2500
2501 let json = r#"[
2503 {"version":"1.0.0","data":{"value":"v1-item"}},
2504 {"version":"2.0.0","data":{"value":"v2-item","count":42}},
2505 {"version":"3.0.0","data":{"value":"v3-item","count":99,"enabled":false}}
2506 ]"#;
2507
2508 let domains: Vec<Domain> = migrator.load_vec("test", json).unwrap();
2509
2510 assert_eq!(domains.len(), 3);
2511
2512 assert_eq!(domains[0].value, "v1-item");
2514 assert_eq!(domains[0].count, 0);
2515 assert!(domains[0].enabled);
2516
2517 assert_eq!(domains[1].value, "v2-item");
2519 assert_eq!(domains[1].count, 42);
2520 assert!(domains[1].enabled);
2521
2522 assert_eq!(domains[2].value, "v3-item");
2524 assert_eq!(domains[2].count, 99);
2525 assert!(!domains[2].enabled);
2526 }
2527
2528 #[test]
2529 fn test_load_vec_from_json_values() {
2530 let path = Migrator::define("test")
2531 .from::<V1>()
2532 .step::<V2>()
2533 .step::<V3>()
2534 .into::<Domain>();
2535
2536 let mut migrator = Migrator::new();
2537 migrator.register(path).unwrap();
2538
2539 let values: Vec<serde_json::Value> = vec![
2541 serde_json::json!({"version":"1.0.0","data":{"value":"direct1"}}),
2542 serde_json::json!({"version":"1.0.0","data":{"value":"direct2"}}),
2543 ];
2544
2545 let domains: Vec<Domain> = migrator.load_vec_from("test", values).unwrap();
2546
2547 assert_eq!(domains.len(), 2);
2548 assert_eq!(domains[0].value, "direct1");
2549 assert_eq!(domains[1].value, "direct2");
2550 }
2551
2552 #[test]
2553 fn test_save_vec_empty() {
2554 let migrator = Migrator::new();
2555 let empty: Vec<V1> = vec![];
2556
2557 let json = migrator.save_vec(empty).unwrap();
2558
2559 assert_eq!(json, "[]");
2560 }
2561
2562 #[test]
2563 fn test_load_vec_invalid_json() {
2564 let path = Migrator::define("test")
2565 .from::<V1>()
2566 .step::<V2>()
2567 .step::<V3>()
2568 .into::<Domain>();
2569
2570 let mut migrator = Migrator::new();
2571 migrator.register(path).unwrap();
2572
2573 let invalid_json = "{ not an array }";
2574 let result: Result<Vec<Domain>, MigrationError> = migrator.load_vec("test", invalid_json);
2575
2576 assert!(matches!(
2577 result,
2578 Err(MigrationError::DeserializationError(_))
2579 ));
2580 }
2581
2582 #[test]
2583 fn test_load_vec_entity_not_found() {
2584 let migrator = Migrator::new();
2585
2586 let json = r#"[{"version":"1.0.0","data":{"value":"test"}}]"#;
2587 let result: Result<Vec<Domain>, MigrationError> = migrator.load_vec("unknown", json);
2588
2589 assert!(matches!(result, Err(MigrationError::EntityNotFound(_))));
2590 }
2591
2592 #[test]
2593 fn test_save_vec_latest_version() {
2594 let migrator = Migrator::new();
2595
2596 let items = vec![
2597 V3 {
2598 value: "latest1".to_string(),
2599 count: 10,
2600 enabled: true,
2601 },
2602 V3 {
2603 value: "latest2".to_string(),
2604 count: 20,
2605 enabled: false,
2606 },
2607 ];
2608
2609 let json = migrator.save_vec(items).unwrap();
2610
2611 assert!(json.contains("\"version\":\"3.0.0\""));
2613 assert!(json.contains("latest1"));
2614 assert!(json.contains("latest2"));
2615 assert!(json.contains("\"count\":10"));
2616 assert!(json.contains("\"count\":20"));
2617 }
2618
2619 #[test]
2620 fn test_load_with_fallback_versioned_data() {
2621 let path = Migrator::define("test")
2622 .from::<V1>()
2623 .step::<V2>()
2624 .step::<V3>()
2625 .into::<Domain>();
2626
2627 let mut migrator = Migrator::new();
2628 migrator.register(path).unwrap();
2629
2630 let versioned_json = r#"{"version":"1.0.0","data":{"value":"versioned_test"}}"#;
2632 let result: Domain = migrator.load_with_fallback("test", versioned_json).unwrap();
2633
2634 assert_eq!(result.value, "versioned_test");
2635 assert_eq!(result.count, 0); assert!(result.enabled); }
2638
2639 #[test]
2640 fn test_load_with_fallback_legacy_data() {
2641 let path = Migrator::define("test")
2642 .from::<V1>()
2643 .step::<V2>()
2644 .step::<V3>()
2645 .into::<Domain>();
2646
2647 let mut migrator = Migrator::new();
2648 migrator.register(path).unwrap();
2649
2650 let legacy_json = r#"{"value":"legacy_test"}"#;
2652 let result: Domain = migrator.load_with_fallback("test", legacy_json).unwrap();
2653
2654 assert_eq!(result.value, "legacy_test");
2655 assert_eq!(result.count, 0); assert!(result.enabled); }
2658
2659 #[test]
2660 fn test_load_with_fallback_mixed_formats() {
2661 let path = Migrator::define("test")
2662 .from::<V2>()
2663 .step::<V3>()
2664 .into::<Domain>();
2665
2666 let mut migrator = Migrator::new();
2667 migrator.register(path).unwrap();
2668
2669 let legacy_v2_json = r#"{"value":"legacy_v2","count":42}"#;
2671 let result: Domain = migrator.load_with_fallback("test", legacy_v2_json).unwrap();
2672
2673 assert_eq!(result.value, "legacy_v2");
2674 assert_eq!(result.count, 42);
2675 assert!(result.enabled); let versioned_json = r#"{"version":"2.0.0","data":{"value":"versioned_v2","count":99}}"#;
2679 let result2: Domain = migrator.load_with_fallback("test", versioned_json).unwrap();
2680
2681 assert_eq!(result2.value, "versioned_v2");
2682 assert_eq!(result2.count, 99);
2683 assert!(result2.enabled);
2684 }
2685
2686 #[test]
2687 fn test_load_from_with_fallback_toml_value() {
2688 let path = Migrator::define("test")
2689 .from::<V1>()
2690 .step::<V2>()
2691 .step::<V3>()
2692 .into::<Domain>();
2693
2694 let mut migrator = Migrator::new();
2695 migrator.register(path).unwrap();
2696
2697 let data = serde_json::json!({"value": "from_toml"});
2699 let result: Domain = migrator.load_from_with_fallback("test", data).unwrap();
2700
2701 assert_eq!(result.value, "from_toml");
2702 assert_eq!(result.count, 0);
2703 assert!(result.enabled);
2704 }
2705
2706 #[test]
2707 fn test_load_with_fallback_no_migration_path() {
2708 let migrator = Migrator::new();
2709
2710 let legacy_json = r#"{"value":"test"}"#;
2711 let result: Result<Domain, MigrationError> =
2712 migrator.load_with_fallback("unknown", legacy_json);
2713
2714 assert!(matches!(result, Err(MigrationError::EntityNotFound(_))));
2715 }
2716
2717 #[test]
2718 fn test_load_with_fallback_empty_migration_path() {
2719 let path = Migrator::define("test").from::<V3>().into::<Domain>();
2721
2722 let mut migrator = Migrator::new();
2723 migrator.register(path).unwrap();
2724
2725 let legacy_json = r#"{"value":"direct","count":10,"enabled":false}"#;
2727 let result: Domain = migrator.load_with_fallback("test", legacy_json).unwrap();
2728
2729 assert_eq!(result.value, "direct");
2730 assert_eq!(result.count, 10);
2731 assert!(!result.enabled);
2732 }
2733
2734 #[test]
2735 fn test_load_with_fallback_invalid_json() {
2736 let path = Migrator::define("test")
2737 .from::<V1>()
2738 .step::<V2>()
2739 .step::<V3>()
2740 .into::<Domain>();
2741
2742 let mut migrator = Migrator::new();
2743 migrator.register(path).unwrap();
2744
2745 let invalid_json = "{ invalid json }";
2746 let result: Result<Domain, MigrationError> =
2747 migrator.load_with_fallback("test", invalid_json);
2748
2749 assert!(matches!(
2750 result,
2751 Err(MigrationError::DeserializationError(_))
2752 ));
2753 }
2754
2755 #[test]
2756 fn test_load_with_fallback_non_object_data() {
2757 let path = Migrator::define("test")
2758 .from::<V1>()
2759 .step::<V2>()
2760 .step::<V3>()
2761 .into::<Domain>();
2762
2763 let mut migrator = Migrator::new();
2764 migrator.register(path).unwrap();
2765
2766 let array_json = r#"["not", "an", "object"]"#;
2768 let result: Result<Domain, MigrationError> =
2769 migrator.load_with_fallback("test", array_json);
2770
2771 assert!(matches!(
2772 result,
2773 Err(MigrationError::DeserializationError(_))
2774 ));
2775 }
2776}