1use crate::errors::MigrationError;
4use crate::{IntoDomain, MigratesTo, Versioned};
5use serde::de::DeserializeOwned;
6use serde::Serialize;
7use std::collections::HashMap;
8use std::marker::PhantomData;
9
10type MigrationFn = Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError>>;
11
12struct EntityMigrationPath {
14 steps: HashMap<String, MigrationFn>,
16 finalize: Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError>>,
18 versions: Vec<String>,
20 version_key: String,
22 data_key: String,
24}
25
26pub struct Migrator {
28 paths: HashMap<String, EntityMigrationPath>,
29 default_version_key: Option<String>,
30 default_data_key: Option<String>,
31}
32
33impl Migrator {
34 pub fn new() -> Self {
36 Self {
37 paths: HashMap::new(),
38 default_version_key: None,
39 default_data_key: None,
40 }
41 }
42
43 pub fn builder() -> MigratorBuilder {
54 MigratorBuilder::new()
55 }
56
57 pub fn define(entity: &str) -> MigrationPathBuilder<Start> {
59 MigrationPathBuilder::new(entity.to_string())
60 }
61
62 pub fn register<D>(&mut self, path: MigrationPath<D>) -> Result<(), MigrationError> {
72 Self::validate_migration_path(&path.entity, &path.versions)?;
73
74 let version_key = path
76 .custom_version_key
77 .or_else(|| self.default_version_key.clone())
78 .unwrap_or_else(|| path.inner.version_key.clone());
79
80 let data_key = path
81 .custom_data_key
82 .or_else(|| self.default_data_key.clone())
83 .unwrap_or_else(|| path.inner.data_key.clone());
84
85 let final_path = EntityMigrationPath {
86 steps: path.inner.steps,
87 finalize: path.inner.finalize,
88 versions: path.versions,
89 version_key,
90 data_key,
91 };
92
93 self.paths.insert(path.entity, final_path);
94 Ok(())
95 }
96
97 fn validate_migration_path(entity: &str, versions: &[String]) -> Result<(), MigrationError> {
99 Self::check_circular_path(entity, versions)?;
101
102 Self::check_version_ordering(entity, versions)?;
104
105 Ok(())
106 }
107
108 fn check_circular_path(entity: &str, versions: &[String]) -> Result<(), MigrationError> {
110 let mut seen = std::collections::HashSet::new();
111
112 for version in versions {
113 if !seen.insert(version) {
114 let path = versions.join(" -> ");
116 return Err(MigrationError::CircularMigrationPath {
117 entity: entity.to_string(),
118 path,
119 });
120 }
121 }
122
123 Ok(())
124 }
125
126 fn check_version_ordering(entity: &str, versions: &[String]) -> Result<(), MigrationError> {
128 for i in 0..versions.len().saturating_sub(1) {
129 let current = &versions[i];
130 let next = &versions[i + 1];
131
132 let current_ver = semver::Version::parse(current).map_err(|e| {
134 MigrationError::DeserializationError(format!("Invalid semver '{}': {}", current, e))
135 })?;
136
137 let next_ver = semver::Version::parse(next).map_err(|e| {
138 MigrationError::DeserializationError(format!("Invalid semver '{}': {}", next, e))
139 })?;
140
141 if next_ver <= current_ver {
143 return Err(MigrationError::InvalidVersionOrder {
144 entity: entity.to_string(),
145 from: current.clone(),
146 to: next.clone(),
147 });
148 }
149 }
150
151 Ok(())
152 }
153
154 pub fn load_from<D, T>(&self, entity: &str, data: T) -> Result<D, MigrationError>
187 where
188 D: DeserializeOwned,
189 T: Serialize,
190 {
191 let value = serde_json::to_value(data).map_err(|e| {
193 MigrationError::DeserializationError(format!(
194 "Failed to convert input data to internal format: {}",
195 e
196 ))
197 })?;
198
199 let path = self
201 .paths
202 .get(entity)
203 .ok_or_else(|| MigrationError::EntityNotFound(entity.to_string()))?;
204
205 let version_key = &path.version_key;
206 let data_key = &path.data_key;
207
208 let obj = value.as_object().ok_or_else(|| {
210 MigrationError::DeserializationError(
211 "Expected object with version and data fields".to_string(),
212 )
213 })?;
214
215 let current_version = obj
216 .get(version_key)
217 .and_then(|v| v.as_str())
218 .ok_or_else(|| {
219 MigrationError::DeserializationError(format!(
220 "Missing or invalid '{}' field",
221 version_key
222 ))
223 })?
224 .to_string();
225
226 let mut current_data = obj
227 .get(data_key)
228 .ok_or_else(|| {
229 MigrationError::DeserializationError(format!("Missing '{}' field", data_key))
230 })?
231 .clone();
232
233 let mut current_version = current_version;
234
235 while let Some(migrate_fn) = path.steps.get(¤t_version) {
237 current_data = migrate_fn(current_data.clone())?;
239
240 match path.versions.iter().position(|v| v == ¤t_version) {
243 Some(idx) if idx + 1 < path.versions.len() => {
244 current_version = path.versions[idx + 1].clone();
245 }
246 _ => break,
247 }
248 }
249
250 let domain_value = (path.finalize)(current_data)?;
252
253 serde_json::from_value(domain_value).map_err(|e| {
254 MigrationError::DeserializationError(format!("Failed to convert to domain: {}", e))
255 })
256 }
257
258 pub fn load<D: DeserializeOwned>(&self, entity: &str, json: &str) -> Result<D, MigrationError> {
286 let data: serde_json::Value = serde_json::from_str(json).map_err(|e| {
287 MigrationError::DeserializationError(format!("Failed to parse JSON: {}", e))
288 })?;
289 self.load_from(entity, data)
290 }
291
292 pub fn load_flat<D: DeserializeOwned>(
320 &self,
321 entity: &str,
322 json: &str,
323 ) -> Result<D, MigrationError> {
324 let data: serde_json::Value = serde_json::from_str(json).map_err(|e| {
325 MigrationError::DeserializationError(format!("Failed to parse JSON: {}", e))
326 })?;
327 self.load_flat_from(entity, data)
328 }
329
330 pub fn load_flat_from<D, T>(&self, entity: &str, value: T) -> Result<D, MigrationError>
359 where
360 D: DeserializeOwned,
361 T: Serialize,
362 {
363 let path = self
364 .paths
365 .get(entity)
366 .ok_or_else(|| MigrationError::EntityNotFound(entity.to_string()))?;
367
368 let version_key = &path.version_key;
369
370 let mut value = serde_json::to_value(value).map_err(|e| {
372 MigrationError::SerializationError(format!("Failed to convert input: {}", e))
373 })?;
374
375 let obj = value.as_object_mut().ok_or_else(|| {
377 MigrationError::DeserializationError(
378 "Expected object with version field at top level".to_string(),
379 )
380 })?;
381
382 let current_version = obj
383 .remove(version_key)
384 .ok_or_else(|| {
385 MigrationError::DeserializationError(format!(
386 "Missing '{}' field in flat format",
387 version_key
388 ))
389 })?
390 .as_str()
391 .ok_or_else(|| {
392 MigrationError::DeserializationError(format!(
393 "Invalid '{}' field type",
394 version_key
395 ))
396 })?
397 .to_string();
398
399 let mut current_data = serde_json::Value::Object(obj.clone());
401 let mut current_version = current_version;
402
403 while let Some(migrate_fn) = path.steps.get(¤t_version) {
405 current_data = migrate_fn(current_data.clone())?;
407
408 match path.versions.iter().position(|v| v == ¤t_version) {
410 Some(idx) if idx + 1 < path.versions.len() => {
411 current_version = path.versions[idx + 1].clone();
412 }
413 _ => break,
414 }
415 }
416
417 let domain_value = (path.finalize)(current_data)?;
419
420 serde_json::from_value(domain_value).map_err(|e| {
421 MigrationError::DeserializationError(format!("Failed to convert to domain: {}", e))
422 })
423 }
424
425 pub fn save<T: Versioned + Serialize>(&self, data: T) -> Result<String, MigrationError> {
456 let version_key = T::VERSION_KEY;
458 let data_key = T::DATA_KEY;
459
460 let data_value = serde_json::to_value(&data).map_err(|e| {
462 MigrationError::SerializationError(format!("Failed to serialize data: {}", e))
463 })?;
464
465 let mut map = serde_json::Map::new();
467 map.insert(
468 version_key.to_string(),
469 serde_json::Value::String(T::VERSION.to_string()),
470 );
471 map.insert(data_key.to_string(), data_value);
472
473 serde_json::to_string(&map).map_err(|e| {
474 MigrationError::SerializationError(format!("Failed to serialize wrapper: {}", e))
475 })
476 }
477
478 pub fn save_flat<T: Versioned + Serialize>(&self, data: T) -> Result<String, MigrationError> {
508 let version_key = T::VERSION_KEY;
509
510 let mut data_value = serde_json::to_value(&data).map_err(|e| {
512 MigrationError::SerializationError(format!("Failed to serialize data: {}", e))
513 })?;
514
515 let obj = data_value.as_object_mut().ok_or_else(|| {
517 MigrationError::SerializationError(
518 "Data must serialize to a JSON object for flat format".to_string(),
519 )
520 })?;
521
522 obj.insert(
524 version_key.to_string(),
525 serde_json::Value::String(T::VERSION.to_string()),
526 );
527
528 serde_json::to_string(&obj).map_err(|e| {
529 MigrationError::SerializationError(format!("Failed to serialize flat format: {}", e))
530 })
531 }
532
533 pub fn load_vec_from<D, T>(&self, entity: &str, data: Vec<T>) -> Result<Vec<D>, MigrationError>
566 where
567 D: DeserializeOwned,
568 T: Serialize,
569 {
570 data.into_iter()
571 .map(|item| self.load_from(entity, item))
572 .collect()
573 }
574
575 pub fn load_vec<D: DeserializeOwned>(
606 &self,
607 entity: &str,
608 json: &str,
609 ) -> Result<Vec<D>, MigrationError> {
610 let data: Vec<serde_json::Value> = serde_json::from_str(json).map_err(|e| {
611 MigrationError::DeserializationError(format!("Failed to parse JSON array: {}", e))
612 })?;
613 self.load_vec_from(entity, data)
614 }
615
616 pub fn load_vec_flat<D: DeserializeOwned>(
647 &self,
648 entity: &str,
649 json: &str,
650 ) -> Result<Vec<D>, MigrationError> {
651 let data: Vec<serde_json::Value> = serde_json::from_str(json).map_err(|e| {
652 MigrationError::DeserializationError(format!("Failed to parse JSON array: {}", e))
653 })?;
654 self.load_vec_flat_from(entity, data)
655 }
656
657 pub fn load_vec_flat_from<D, T>(
686 &self,
687 entity: &str,
688 data: Vec<T>,
689 ) -> Result<Vec<D>, MigrationError>
690 where
691 D: DeserializeOwned,
692 T: Serialize,
693 {
694 data.into_iter()
695 .map(|item| self.load_flat_from(entity, item))
696 .collect()
697 }
698
699 pub fn save_vec<T: Versioned + Serialize>(
736 &self,
737 data: Vec<T>,
738 ) -> Result<String, MigrationError> {
739 let version_key = T::VERSION_KEY;
740 let data_key = T::DATA_KEY;
741
742 let wrappers: Result<Vec<serde_json::Value>, MigrationError> = data
743 .into_iter()
744 .map(|item| {
745 let data_value = serde_json::to_value(&item).map_err(|e| {
746 MigrationError::SerializationError(format!("Failed to serialize item: {}", e))
747 })?;
748
749 let mut map = serde_json::Map::new();
750 map.insert(
751 version_key.to_string(),
752 serde_json::Value::String(T::VERSION.to_string()),
753 );
754 map.insert(data_key.to_string(), data_value);
755
756 Ok(serde_json::Value::Object(map))
757 })
758 .collect();
759
760 serde_json::to_string(&wrappers?).map_err(|e| {
761 MigrationError::SerializationError(format!("Failed to serialize data array: {}", e))
762 })
763 }
764
765 pub fn save_vec_flat<T: Versioned + Serialize>(
801 &self,
802 data: Vec<T>,
803 ) -> Result<String, MigrationError> {
804 let version_key = T::VERSION_KEY;
805
806 let flat_items: Result<Vec<serde_json::Value>, MigrationError> = data
807 .into_iter()
808 .map(|item| {
809 let mut data_value = serde_json::to_value(&item).map_err(|e| {
810 MigrationError::SerializationError(format!("Failed to serialize item: {}", e))
811 })?;
812
813 let obj = data_value.as_object_mut().ok_or_else(|| {
814 MigrationError::SerializationError(
815 "Data must serialize to a JSON object for flat format".to_string(),
816 )
817 })?;
818
819 obj.insert(
821 version_key.to_string(),
822 serde_json::Value::String(T::VERSION.to_string()),
823 );
824
825 Ok(serde_json::Value::Object(obj.clone()))
826 })
827 .collect();
828
829 serde_json::to_string(&flat_items?).map_err(|e| {
830 MigrationError::SerializationError(format!(
831 "Failed to serialize flat data array: {}",
832 e
833 ))
834 })
835 }
836}
837
838impl Default for Migrator {
839 fn default() -> Self {
840 Self::new()
841 }
842}
843
844pub struct MigratorBuilder {
846 default_version_key: Option<String>,
847 default_data_key: Option<String>,
848}
849
850impl MigratorBuilder {
851 pub(crate) fn new() -> Self {
852 Self {
853 default_version_key: None,
854 default_data_key: None,
855 }
856 }
857
858 pub fn default_version_key(mut self, key: impl Into<String>) -> Self {
864 self.default_version_key = Some(key.into());
865 self
866 }
867
868 pub fn default_data_key(mut self, key: impl Into<String>) -> Self {
874 self.default_data_key = Some(key.into());
875 self
876 }
877
878 pub fn build(self) -> Migrator {
880 Migrator {
881 paths: HashMap::new(),
882 default_version_key: self.default_version_key,
883 default_data_key: self.default_data_key,
884 }
885 }
886}
887
888pub struct Start;
890
891pub struct HasFrom<V>(PhantomData<V>);
893
894pub struct HasSteps<V>(PhantomData<V>);
896
897pub struct MigrationPathBuilder<State> {
899 entity: String,
900 steps: HashMap<String, MigrationFn>,
901 versions: Vec<String>,
902 version_key: String,
903 data_key: String,
904 custom_version_key: Option<String>,
905 custom_data_key: Option<String>,
906 _state: PhantomData<State>,
907}
908
909impl MigrationPathBuilder<Start> {
910 fn new(entity: String) -> Self {
911 Self {
912 entity,
913 steps: HashMap::new(),
914 versions: Vec::new(),
915 version_key: String::from("version"),
916 data_key: String::from("data"),
917 custom_version_key: None,
918 custom_data_key: None,
919 _state: PhantomData,
920 }
921 }
922
923 pub fn with_keys(
936 mut self,
937 version_key: impl Into<String>,
938 data_key: impl Into<String>,
939 ) -> Self {
940 self.custom_version_key = Some(version_key.into());
941 self.custom_data_key = Some(data_key.into());
942 self
943 }
944
945 pub fn from<V: Versioned + DeserializeOwned>(self) -> MigrationPathBuilder<HasFrom<V>> {
947 let mut versions = self.versions;
948 versions.push(V::VERSION.to_string());
949
950 MigrationPathBuilder {
951 entity: self.entity,
952 steps: self.steps,
953 versions,
954 version_key: V::VERSION_KEY.to_string(),
955 data_key: V::DATA_KEY.to_string(),
956 custom_version_key: self.custom_version_key,
957 custom_data_key: self.custom_data_key,
958 _state: PhantomData,
959 }
960 }
961}
962
963impl<V> MigrationPathBuilder<HasFrom<V>>
964where
965 V: Versioned + DeserializeOwned,
966{
967 pub fn step<Next>(mut self) -> MigrationPathBuilder<HasSteps<Next>>
969 where
970 V: MigratesTo<Next>,
971 Next: Versioned + DeserializeOwned + Serialize,
972 {
973 let from_version = V::VERSION.to_string();
974 let migration_fn: MigrationFn = Box::new(move |value| {
975 let from_value: V = serde_json::from_value(value).map_err(|e| {
976 MigrationError::DeserializationError(format!(
977 "Failed to deserialize version {}: {}",
978 V::VERSION,
979 e
980 ))
981 })?;
982
983 let to_value = from_value.migrate();
984
985 serde_json::to_value(&to_value).map_err(|e| MigrationError::MigrationStepFailed {
987 from: V::VERSION.to_string(),
988 to: Next::VERSION.to_string(),
989 error: e.to_string(),
990 })
991 });
992
993 self.steps.insert(from_version, migration_fn);
994 self.versions.push(Next::VERSION.to_string());
995
996 MigrationPathBuilder {
997 entity: self.entity,
998 steps: self.steps,
999 versions: self.versions,
1000 version_key: self.version_key,
1001 data_key: self.data_key,
1002 custom_version_key: self.custom_version_key,
1003 custom_data_key: self.custom_data_key,
1004 _state: PhantomData,
1005 }
1006 }
1007
1008 pub fn into<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1010 where
1011 V: IntoDomain<D>,
1012 {
1013 let finalize: Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError>> =
1014 Box::new(move |value| {
1015 let versioned: V = serde_json::from_value(value).map_err(|e| {
1016 MigrationError::DeserializationError(format!(
1017 "Failed to deserialize final version: {}",
1018 e
1019 ))
1020 })?;
1021
1022 let domain = versioned.into_domain();
1023
1024 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1025 from: V::VERSION.to_string(),
1026 to: "domain".to_string(),
1027 error: e.to_string(),
1028 })
1029 });
1030
1031 MigrationPath {
1032 entity: self.entity,
1033 inner: EntityMigrationPath {
1034 steps: self.steps,
1035 finalize,
1036 versions: self.versions.clone(),
1037 version_key: self.version_key,
1038 data_key: self.data_key,
1039 },
1040 versions: self.versions,
1041 custom_version_key: self.custom_version_key,
1042 custom_data_key: self.custom_data_key,
1043 _phantom: PhantomData,
1044 }
1045 }
1046}
1047
1048impl<V> MigrationPathBuilder<HasSteps<V>>
1049where
1050 V: Versioned + DeserializeOwned,
1051{
1052 pub fn step<Next>(mut self) -> MigrationPathBuilder<HasSteps<Next>>
1054 where
1055 V: MigratesTo<Next>,
1056 Next: Versioned + DeserializeOwned + Serialize,
1057 {
1058 let from_version = V::VERSION.to_string();
1059 let migration_fn: MigrationFn = Box::new(move |value| {
1060 let from_value: V = serde_json::from_value(value).map_err(|e| {
1061 MigrationError::DeserializationError(format!(
1062 "Failed to deserialize version {}: {}",
1063 V::VERSION,
1064 e
1065 ))
1066 })?;
1067
1068 let to_value = from_value.migrate();
1069
1070 serde_json::to_value(&to_value).map_err(|e| MigrationError::MigrationStepFailed {
1072 from: V::VERSION.to_string(),
1073 to: Next::VERSION.to_string(),
1074 error: e.to_string(),
1075 })
1076 });
1077
1078 self.steps.insert(from_version, migration_fn);
1079 self.versions.push(Next::VERSION.to_string());
1080
1081 MigrationPathBuilder {
1082 entity: self.entity,
1083 steps: self.steps,
1084 versions: self.versions,
1085 version_key: self.version_key,
1086 data_key: self.data_key,
1087 custom_version_key: self.custom_version_key,
1088 custom_data_key: self.custom_data_key,
1089 _state: PhantomData,
1090 }
1091 }
1092
1093 pub fn into<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1095 where
1096 V: IntoDomain<D>,
1097 {
1098 let finalize: Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError>> =
1099 Box::new(move |value| {
1100 let versioned: V = serde_json::from_value(value).map_err(|e| {
1101 MigrationError::DeserializationError(format!(
1102 "Failed to deserialize final version: {}",
1103 e
1104 ))
1105 })?;
1106
1107 let domain = versioned.into_domain();
1108
1109 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1110 from: V::VERSION.to_string(),
1111 to: "domain".to_string(),
1112 error: e.to_string(),
1113 })
1114 });
1115
1116 MigrationPath {
1117 entity: self.entity,
1118 inner: EntityMigrationPath {
1119 steps: self.steps,
1120 finalize,
1121 versions: self.versions.clone(),
1122 version_key: self.version_key,
1123 data_key: self.data_key,
1124 },
1125 versions: self.versions,
1126 custom_version_key: self.custom_version_key,
1127 custom_data_key: self.custom_data_key,
1128 _phantom: PhantomData,
1129 }
1130 }
1131}
1132
1133pub struct MigrationPath<D> {
1135 entity: String,
1136 inner: EntityMigrationPath,
1137 versions: Vec<String>,
1139 custom_version_key: Option<String>,
1141 custom_data_key: Option<String>,
1143 _phantom: PhantomData<D>,
1144}
1145
1146pub struct ConfigMigrator {
1181 root: serde_json::Value,
1182 migrator: Migrator,
1183}
1184
1185impl ConfigMigrator {
1186 pub fn from(json: &str, migrator: Migrator) -> Result<Self, MigrationError> {
1192 let root = serde_json::from_str(json)
1193 .map_err(|e| MigrationError::DeserializationError(e.to_string()))?;
1194 Ok(Self { root, migrator })
1195 }
1196
1197 pub fn query<T>(&self, key: &str) -> Result<Vec<T>, MigrationError>
1217 where
1218 T: crate::Queryable + for<'de> serde::Deserialize<'de>,
1219 {
1220 let value = &self.root[key];
1221 if value.is_null() {
1222 return Ok(Vec::new());
1223 }
1224
1225 if !value.is_array() {
1226 return Err(MigrationError::DeserializationError(format!(
1227 "Key '{}' does not contain an array",
1228 key
1229 )));
1230 }
1231
1232 let array = value.as_array().unwrap(); self.migrator
1234 .load_vec_flat_from(T::ENTITY_NAME, array.to_vec())
1235 }
1236
1237 pub fn update<T>(&mut self, key: &str, data: Vec<T>) -> Result<(), MigrationError>
1256 where
1257 T: crate::Queryable + crate::Versioned + serde::Serialize,
1258 {
1259 let json = self.migrator.save_vec_flat(data)?;
1260 let value: serde_json::Value = serde_json::from_str(&json)
1261 .map_err(|e| MigrationError::SerializationError(e.to_string()))?;
1262 self.root[key] = value;
1263 Ok(())
1264 }
1265
1266 pub fn to_string(&self) -> Result<String, MigrationError> {
1272 serde_json::to_string_pretty(&self.root)
1273 .map_err(|e| MigrationError::SerializationError(e.to_string()))
1274 }
1275
1276 pub fn to_string_compact(&self) -> Result<String, MigrationError> {
1282 serde_json::to_string(&self.root)
1283 .map_err(|e| MigrationError::SerializationError(e.to_string()))
1284 }
1285
1286 pub fn as_value(&self) -> &serde_json::Value {
1288 &self.root
1289 }
1290}
1291
1292#[cfg(test)]
1293mod tests {
1294 use super::*;
1295 use crate::{IntoDomain, MigratesTo, Versioned, VersionedWrapper};
1296 use serde::{Deserialize, Serialize};
1297
1298 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1300 struct V1 {
1301 value: String,
1302 }
1303
1304 impl Versioned for V1 {
1305 const VERSION: &'static str = "1.0.0";
1306 }
1307
1308 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1309 struct V2 {
1310 value: String,
1311 count: u32,
1312 }
1313
1314 impl Versioned for V2 {
1315 const VERSION: &'static str = "2.0.0";
1316 }
1317
1318 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1319 struct V3 {
1320 value: String,
1321 count: u32,
1322 enabled: bool,
1323 }
1324
1325 impl Versioned for V3 {
1326 const VERSION: &'static str = "3.0.0";
1327 }
1328
1329 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1330 struct Domain {
1331 value: String,
1332 count: u32,
1333 enabled: bool,
1334 }
1335
1336 impl MigratesTo<V2> for V1 {
1337 fn migrate(self) -> V2 {
1338 V2 {
1339 value: self.value,
1340 count: 0,
1341 }
1342 }
1343 }
1344
1345 impl MigratesTo<V3> for V2 {
1346 fn migrate(self) -> V3 {
1347 V3 {
1348 value: self.value,
1349 count: self.count,
1350 enabled: true,
1351 }
1352 }
1353 }
1354
1355 impl IntoDomain<Domain> for V3 {
1356 fn into_domain(self) -> Domain {
1357 Domain {
1358 value: self.value,
1359 count: self.count,
1360 enabled: self.enabled,
1361 }
1362 }
1363 }
1364
1365 #[test]
1366 fn test_migrator_new() {
1367 let migrator = Migrator::new();
1368 assert_eq!(migrator.paths.len(), 0);
1369 }
1370
1371 #[test]
1372 fn test_migrator_default() {
1373 let migrator = Migrator::default();
1374 assert_eq!(migrator.paths.len(), 0);
1375 }
1376
1377 #[test]
1378 fn test_single_step_migration() {
1379 let path = Migrator::define("test")
1380 .from::<V2>()
1381 .step::<V3>()
1382 .into::<Domain>();
1383
1384 let mut migrator = Migrator::new();
1385 migrator.register(path).unwrap();
1386
1387 let v2 = V2 {
1388 value: "test".to_string(),
1389 count: 42,
1390 };
1391 let wrapper = VersionedWrapper::from_versioned(v2);
1392 let json = serde_json::to_string(&wrapper).unwrap();
1393
1394 let result: Domain = migrator.load("test", &json).unwrap();
1395 assert_eq!(result.value, "test");
1396 assert_eq!(result.count, 42);
1397 assert!(result.enabled);
1398 }
1399
1400 #[test]
1401 fn test_multi_step_migration() {
1402 let path = Migrator::define("test")
1403 .from::<V1>()
1404 .step::<V2>()
1405 .step::<V3>()
1406 .into::<Domain>();
1407
1408 let mut migrator = Migrator::new();
1409 migrator.register(path).unwrap();
1410
1411 let v1 = V1 {
1412 value: "multi_step".to_string(),
1413 };
1414 let wrapper = VersionedWrapper::from_versioned(v1);
1415 let json = serde_json::to_string(&wrapper).unwrap();
1416
1417 let result: Domain = migrator.load("test", &json).unwrap();
1418 assert_eq!(result.value, "multi_step");
1419 assert_eq!(result.count, 0);
1420 assert!(result.enabled);
1421 }
1422
1423 #[test]
1424 fn test_no_migration_needed() {
1425 let path = Migrator::define("test").from::<V3>().into::<Domain>();
1426
1427 let mut migrator = Migrator::new();
1428 migrator.register(path).unwrap();
1429
1430 let v3 = V3 {
1431 value: "latest".to_string(),
1432 count: 100,
1433 enabled: false,
1434 };
1435 let wrapper = VersionedWrapper::from_versioned(v3);
1436 let json = serde_json::to_string(&wrapper).unwrap();
1437
1438 let result: Domain = migrator.load("test", &json).unwrap();
1439 assert_eq!(result.value, "latest");
1440 assert_eq!(result.count, 100);
1441 assert!(!result.enabled);
1442 }
1443
1444 #[test]
1445 fn test_entity_not_found() {
1446 let migrator = Migrator::new();
1447
1448 let v1 = V1 {
1449 value: "test".to_string(),
1450 };
1451 let wrapper = VersionedWrapper::from_versioned(v1);
1452 let json = serde_json::to_string(&wrapper).unwrap();
1453
1454 let result: Result<Domain, MigrationError> = migrator.load("unknown", &json);
1455 assert!(matches!(result, Err(MigrationError::EntityNotFound(_))));
1456
1457 if let Err(MigrationError::EntityNotFound(entity)) = result {
1458 assert_eq!(entity, "unknown");
1459 }
1460 }
1461
1462 #[test]
1463 fn test_invalid_json() {
1464 let path = Migrator::define("test").from::<V3>().into::<Domain>();
1465
1466 let mut migrator = Migrator::new();
1467 migrator.register(path).unwrap();
1468
1469 let invalid_json = "{ invalid json }";
1470 let result: Result<Domain, MigrationError> = migrator.load("test", invalid_json);
1471
1472 assert!(matches!(
1473 result,
1474 Err(MigrationError::DeserializationError(_))
1475 ));
1476 }
1477
1478 #[test]
1479 fn test_multiple_entities() {
1480 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1481 struct OtherDomain {
1482 value: String,
1483 }
1484
1485 impl IntoDomain<OtherDomain> for V1 {
1486 fn into_domain(self) -> OtherDomain {
1487 OtherDomain { value: self.value }
1488 }
1489 }
1490
1491 let path1 = Migrator::define("entity1")
1492 .from::<V1>()
1493 .step::<V2>()
1494 .step::<V3>()
1495 .into::<Domain>();
1496
1497 let path2 = Migrator::define("entity2")
1498 .from::<V1>()
1499 .into::<OtherDomain>();
1500
1501 let mut migrator = Migrator::new();
1502 migrator.register(path1).unwrap();
1503 migrator.register(path2).unwrap();
1504
1505 let v1 = V1 {
1507 value: "entity1".to_string(),
1508 };
1509 let wrapper = VersionedWrapper::from_versioned(v1);
1510 let json = serde_json::to_string(&wrapper).unwrap();
1511 let result: Domain = migrator.load("entity1", &json).unwrap();
1512 assert_eq!(result.value, "entity1");
1513
1514 let v1 = V1 {
1516 value: "entity2".to_string(),
1517 };
1518 let wrapper = VersionedWrapper::from_versioned(v1);
1519 let json = serde_json::to_string(&wrapper).unwrap();
1520 let result: OtherDomain = migrator.load("entity2", &json).unwrap();
1521 assert_eq!(result.value, "entity2");
1522 }
1523
1524 #[test]
1525 fn test_save() {
1526 let migrator = Migrator::new();
1527
1528 let v1 = V1 {
1529 value: "test_save".to_string(),
1530 };
1531
1532 let json = migrator.save(v1).unwrap();
1533
1534 assert!(json.contains("\"version\""));
1536 assert!(json.contains("\"1.0.0\""));
1537 assert!(json.contains("\"data\""));
1538 assert!(json.contains("\"test_save\""));
1539
1540 let parsed: VersionedWrapper<serde_json::Value> = serde_json::from_str(&json).unwrap();
1542 assert_eq!(parsed.version, "1.0.0");
1543 }
1544
1545 #[test]
1546 fn test_save_and_load_roundtrip() {
1547 let path = Migrator::define("test")
1548 .from::<V1>()
1549 .step::<V2>()
1550 .step::<V3>()
1551 .into::<Domain>();
1552
1553 let mut migrator = Migrator::new();
1554 migrator.register(path).unwrap();
1555
1556 let v1 = V1 {
1558 value: "roundtrip".to_string(),
1559 };
1560 let json = migrator.save(v1).unwrap();
1561
1562 let domain: Domain = migrator.load("test", &json).unwrap();
1564
1565 assert_eq!(domain.value, "roundtrip");
1566 assert_eq!(domain.count, 0); assert!(domain.enabled); }
1569
1570 #[test]
1571 fn test_save_latest_version() {
1572 let migrator = Migrator::new();
1573
1574 let v3 = V3 {
1575 value: "latest".to_string(),
1576 count: 42,
1577 enabled: false,
1578 };
1579
1580 let json = migrator.save(v3).unwrap();
1581
1582 assert!(json.contains("\"version\":\"3.0.0\""));
1584 assert!(json.contains("\"value\":\"latest\""));
1585 assert!(json.contains("\"count\":42"));
1586 assert!(json.contains("\"enabled\":false"));
1587 }
1588
1589 #[test]
1590 fn test_save_pretty() {
1591 let migrator = Migrator::new();
1592
1593 let v2 = V2 {
1594 value: "pretty".to_string(),
1595 count: 10,
1596 };
1597
1598 let json = migrator.save(v2).unwrap();
1599
1600 assert!(!json.contains('\n'));
1602 assert!(json.contains("\"version\":\"2.0.0\""));
1603 }
1604
1605 #[test]
1606 fn test_validation_invalid_version_order() {
1607 let entity = "test".to_string();
1609 let versions = vec!["2.0.0".to_string(), "1.0.0".to_string()]; let result = Migrator::validate_migration_path(&entity, &versions);
1612 assert!(matches!(
1613 result,
1614 Err(MigrationError::InvalidVersionOrder { .. })
1615 ));
1616
1617 if let Err(MigrationError::InvalidVersionOrder {
1618 entity: e,
1619 from,
1620 to,
1621 }) = result
1622 {
1623 assert_eq!(e, "test");
1624 assert_eq!(from, "2.0.0");
1625 assert_eq!(to, "1.0.0");
1626 }
1627 }
1628
1629 #[test]
1630 fn test_validation_circular_path() {
1631 let entity = "test".to_string();
1633 let versions = vec![
1634 "1.0.0".to_string(),
1635 "2.0.0".to_string(),
1636 "1.0.0".to_string(), ];
1638
1639 let result = Migrator::validate_migration_path(&entity, &versions);
1640 assert!(matches!(
1641 result,
1642 Err(MigrationError::CircularMigrationPath { .. })
1643 ));
1644
1645 if let Err(MigrationError::CircularMigrationPath { entity: e, path }) = result {
1646 assert_eq!(e, "test");
1647 assert!(path.contains("1.0.0"));
1648 assert!(path.contains("2.0.0"));
1649 }
1650 }
1651
1652 #[test]
1653 fn test_validation_valid_path() {
1654 let entity = "test".to_string();
1656 let versions = vec![
1657 "1.0.0".to_string(),
1658 "1.1.0".to_string(),
1659 "2.0.0".to_string(),
1660 ];
1661
1662 let result = Migrator::validate_migration_path(&entity, &versions);
1663 assert!(result.is_ok());
1664 }
1665
1666 #[test]
1667 fn test_validation_empty_path() {
1668 let entity = "test".to_string();
1670 let versions = vec![];
1671
1672 let result = Migrator::validate_migration_path(&entity, &versions);
1673 assert!(result.is_ok());
1674 }
1675
1676 #[test]
1677 fn test_validation_single_version() {
1678 let entity = "test".to_string();
1680 let versions = vec!["1.0.0".to_string()];
1681
1682 let result = Migrator::validate_migration_path(&entity, &versions);
1683 assert!(result.is_ok());
1684 }
1685
1686 #[test]
1688 fn test_save_vec_and_load_vec() {
1689 let migrator = Migrator::new();
1690
1691 let items = vec![
1693 V1 {
1694 value: "item1".to_string(),
1695 },
1696 V1 {
1697 value: "item2".to_string(),
1698 },
1699 V1 {
1700 value: "item3".to_string(),
1701 },
1702 ];
1703
1704 let json = migrator.save_vec(items).unwrap();
1705
1706 assert!(json.starts_with('['));
1708 assert!(json.ends_with(']'));
1709 assert!(json.contains("\"version\":\"1.0.0\""));
1710 assert!(json.contains("item1"));
1711 assert!(json.contains("item2"));
1712 assert!(json.contains("item3"));
1713
1714 let path = Migrator::define("test")
1716 .from::<V1>()
1717 .step::<V2>()
1718 .step::<V3>()
1719 .into::<Domain>();
1720
1721 let mut migrator = Migrator::new();
1722 migrator.register(path).unwrap();
1723
1724 let domains: Vec<Domain> = migrator.load_vec("test", &json).unwrap();
1726
1727 assert_eq!(domains.len(), 3);
1728 assert_eq!(domains[0].value, "item1");
1729 assert_eq!(domains[1].value, "item2");
1730 assert_eq!(domains[2].value, "item3");
1731
1732 for domain in &domains {
1734 assert_eq!(domain.count, 0);
1735 assert!(domain.enabled);
1736 }
1737 }
1738
1739 #[test]
1740 fn test_load_vec_empty_array() {
1741 let path = Migrator::define("test")
1742 .from::<V1>()
1743 .step::<V2>()
1744 .step::<V3>()
1745 .into::<Domain>();
1746
1747 let mut migrator = Migrator::new();
1748 migrator.register(path).unwrap();
1749
1750 let json = "[]";
1751 let domains: Vec<Domain> = migrator.load_vec("test", json).unwrap();
1752
1753 assert_eq!(domains.len(), 0);
1754 }
1755
1756 #[test]
1757 fn test_load_vec_mixed_versions() {
1758 let path = Migrator::define("test")
1760 .from::<V1>()
1761 .step::<V2>()
1762 .step::<V3>()
1763 .into::<Domain>();
1764
1765 let mut migrator = Migrator::new();
1766 migrator.register(path).unwrap();
1767
1768 let json = r#"[
1770 {"version":"1.0.0","data":{"value":"v1-item"}},
1771 {"version":"2.0.0","data":{"value":"v2-item","count":42}},
1772 {"version":"3.0.0","data":{"value":"v3-item","count":99,"enabled":false}}
1773 ]"#;
1774
1775 let domains: Vec<Domain> = migrator.load_vec("test", json).unwrap();
1776
1777 assert_eq!(domains.len(), 3);
1778
1779 assert_eq!(domains[0].value, "v1-item");
1781 assert_eq!(domains[0].count, 0);
1782 assert!(domains[0].enabled);
1783
1784 assert_eq!(domains[1].value, "v2-item");
1786 assert_eq!(domains[1].count, 42);
1787 assert!(domains[1].enabled);
1788
1789 assert_eq!(domains[2].value, "v3-item");
1791 assert_eq!(domains[2].count, 99);
1792 assert!(!domains[2].enabled);
1793 }
1794
1795 #[test]
1796 fn test_load_vec_from_json_values() {
1797 let path = Migrator::define("test")
1798 .from::<V1>()
1799 .step::<V2>()
1800 .step::<V3>()
1801 .into::<Domain>();
1802
1803 let mut migrator = Migrator::new();
1804 migrator.register(path).unwrap();
1805
1806 let values: Vec<serde_json::Value> = vec![
1808 serde_json::json!({"version":"1.0.0","data":{"value":"direct1"}}),
1809 serde_json::json!({"version":"1.0.0","data":{"value":"direct2"}}),
1810 ];
1811
1812 let domains: Vec<Domain> = migrator.load_vec_from("test", values).unwrap();
1813
1814 assert_eq!(domains.len(), 2);
1815 assert_eq!(domains[0].value, "direct1");
1816 assert_eq!(domains[1].value, "direct2");
1817 }
1818
1819 #[test]
1820 fn test_save_vec_empty() {
1821 let migrator = Migrator::new();
1822 let empty: Vec<V1> = vec![];
1823
1824 let json = migrator.save_vec(empty).unwrap();
1825
1826 assert_eq!(json, "[]");
1827 }
1828
1829 #[test]
1830 fn test_load_vec_invalid_json() {
1831 let path = Migrator::define("test")
1832 .from::<V1>()
1833 .step::<V2>()
1834 .step::<V3>()
1835 .into::<Domain>();
1836
1837 let mut migrator = Migrator::new();
1838 migrator.register(path).unwrap();
1839
1840 let invalid_json = "{ not an array }";
1841 let result: Result<Vec<Domain>, MigrationError> = migrator.load_vec("test", invalid_json);
1842
1843 assert!(matches!(
1844 result,
1845 Err(MigrationError::DeserializationError(_))
1846 ));
1847 }
1848
1849 #[test]
1850 fn test_load_vec_entity_not_found() {
1851 let migrator = Migrator::new();
1852
1853 let json = r#"[{"version":"1.0.0","data":{"value":"test"}}]"#;
1854 let result: Result<Vec<Domain>, MigrationError> = migrator.load_vec("unknown", json);
1855
1856 assert!(matches!(result, Err(MigrationError::EntityNotFound(_))));
1857 }
1858
1859 #[test]
1860 fn test_save_vec_latest_version() {
1861 let migrator = Migrator::new();
1862
1863 let items = vec![
1864 V3 {
1865 value: "latest1".to_string(),
1866 count: 10,
1867 enabled: true,
1868 },
1869 V3 {
1870 value: "latest2".to_string(),
1871 count: 20,
1872 enabled: false,
1873 },
1874 ];
1875
1876 let json = migrator.save_vec(items).unwrap();
1877
1878 assert!(json.contains("\"version\":\"3.0.0\""));
1880 assert!(json.contains("latest1"));
1881 assert!(json.contains("latest2"));
1882 assert!(json.contains("\"count\":10"));
1883 assert!(json.contains("\"count\":20"));
1884 }
1885}