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 get_latest_version(&self, entity: &str) -> Option<&str> {
49 self.paths
50 .get(entity)
51 .and_then(|path| path.versions.last())
52 .map(|v| v.as_str())
53 }
54
55 pub fn builder() -> MigratorBuilder {
66 MigratorBuilder::new()
67 }
68
69 pub fn define(entity: &str) -> MigrationPathBuilder<Start> {
71 MigrationPathBuilder::new(entity.to_string())
72 }
73
74 pub fn register<D>(&mut self, path: MigrationPath<D>) -> Result<(), MigrationError> {
84 Self::validate_migration_path(&path.entity, &path.versions)?;
85
86 let version_key = path
88 .custom_version_key
89 .or_else(|| self.default_version_key.clone())
90 .unwrap_or_else(|| path.inner.version_key.clone());
91
92 let data_key = path
93 .custom_data_key
94 .or_else(|| self.default_data_key.clone())
95 .unwrap_or_else(|| path.inner.data_key.clone());
96
97 let final_path = EntityMigrationPath {
98 steps: path.inner.steps,
99 finalize: path.inner.finalize,
100 versions: path.versions,
101 version_key,
102 data_key,
103 };
104
105 self.paths.insert(path.entity, final_path);
106 Ok(())
107 }
108
109 fn validate_migration_path(entity: &str, versions: &[String]) -> Result<(), MigrationError> {
111 Self::check_circular_path(entity, versions)?;
113
114 Self::check_version_ordering(entity, versions)?;
116
117 Ok(())
118 }
119
120 fn check_circular_path(entity: &str, versions: &[String]) -> Result<(), MigrationError> {
122 let mut seen = std::collections::HashSet::new();
123
124 for version in versions {
125 if !seen.insert(version) {
126 let path = versions.join(" -> ");
128 return Err(MigrationError::CircularMigrationPath {
129 entity: entity.to_string(),
130 path,
131 });
132 }
133 }
134
135 Ok(())
136 }
137
138 fn check_version_ordering(entity: &str, versions: &[String]) -> Result<(), MigrationError> {
140 for i in 0..versions.len().saturating_sub(1) {
141 let current = &versions[i];
142 let next = &versions[i + 1];
143
144 let current_ver = semver::Version::parse(current).map_err(|e| {
146 MigrationError::DeserializationError(format!("Invalid semver '{}': {}", current, e))
147 })?;
148
149 let next_ver = semver::Version::parse(next).map_err(|e| {
150 MigrationError::DeserializationError(format!("Invalid semver '{}': {}", next, e))
151 })?;
152
153 if next_ver <= current_ver {
155 return Err(MigrationError::InvalidVersionOrder {
156 entity: entity.to_string(),
157 from: current.clone(),
158 to: next.clone(),
159 });
160 }
161 }
162
163 Ok(())
164 }
165
166 pub fn load_from<D, T>(&self, entity: &str, data: T) -> Result<D, MigrationError>
199 where
200 D: DeserializeOwned,
201 T: Serialize,
202 {
203 let value = serde_json::to_value(data).map_err(|e| {
205 MigrationError::DeserializationError(format!(
206 "Failed to convert input data to internal format: {}",
207 e
208 ))
209 })?;
210
211 let path = self
213 .paths
214 .get(entity)
215 .ok_or_else(|| MigrationError::EntityNotFound(entity.to_string()))?;
216
217 let version_key = &path.version_key;
218 let data_key = &path.data_key;
219
220 let obj = value.as_object().ok_or_else(|| {
222 MigrationError::DeserializationError(
223 "Expected object with version and data fields".to_string(),
224 )
225 })?;
226
227 let current_version = obj
228 .get(version_key)
229 .and_then(|v| v.as_str())
230 .ok_or_else(|| {
231 MigrationError::DeserializationError(format!(
232 "Missing or invalid '{}' field",
233 version_key
234 ))
235 })?
236 .to_string();
237
238 let mut current_data = obj
239 .get(data_key)
240 .ok_or_else(|| {
241 MigrationError::DeserializationError(format!("Missing '{}' field", data_key))
242 })?
243 .clone();
244
245 let mut current_version = current_version;
246
247 while let Some(migrate_fn) = path.steps.get(¤t_version) {
249 current_data = migrate_fn(current_data.clone())?;
251
252 match path.versions.iter().position(|v| v == ¤t_version) {
255 Some(idx) if idx + 1 < path.versions.len() => {
256 current_version = path.versions[idx + 1].clone();
257 }
258 _ => break,
259 }
260 }
261
262 let domain_value = (path.finalize)(current_data)?;
264
265 serde_json::from_value(domain_value).map_err(|e| {
266 MigrationError::DeserializationError(format!("Failed to convert to domain: {}", e))
267 })
268 }
269
270 pub fn load<D: DeserializeOwned>(&self, entity: &str, json: &str) -> Result<D, MigrationError> {
298 let data: serde_json::Value = serde_json::from_str(json).map_err(|e| {
299 MigrationError::DeserializationError(format!("Failed to parse JSON: {}", e))
300 })?;
301 self.load_from(entity, data)
302 }
303
304 pub fn load_flat<D: DeserializeOwned>(
332 &self,
333 entity: &str,
334 json: &str,
335 ) -> Result<D, MigrationError> {
336 let data: serde_json::Value = serde_json::from_str(json).map_err(|e| {
337 MigrationError::DeserializationError(format!("Failed to parse JSON: {}", e))
338 })?;
339 self.load_flat_from(entity, data)
340 }
341
342 pub fn load_flat_from<D, T>(&self, entity: &str, value: T) -> Result<D, MigrationError>
371 where
372 D: DeserializeOwned,
373 T: Serialize,
374 {
375 let path = self
376 .paths
377 .get(entity)
378 .ok_or_else(|| MigrationError::EntityNotFound(entity.to_string()))?;
379
380 let version_key = &path.version_key;
381
382 let mut value = serde_json::to_value(value).map_err(|e| {
384 MigrationError::SerializationError(format!("Failed to convert input: {}", e))
385 })?;
386
387 let obj = value.as_object_mut().ok_or_else(|| {
389 MigrationError::DeserializationError(
390 "Expected object with version field at top level".to_string(),
391 )
392 })?;
393
394 let current_version = obj
395 .remove(version_key)
396 .ok_or_else(|| {
397 MigrationError::DeserializationError(format!(
398 "Missing '{}' field in flat format",
399 version_key
400 ))
401 })?
402 .as_str()
403 .ok_or_else(|| {
404 MigrationError::DeserializationError(format!(
405 "Invalid '{}' field type",
406 version_key
407 ))
408 })?
409 .to_string();
410
411 let mut current_data = serde_json::Value::Object(obj.clone());
413 let mut current_version = current_version;
414
415 while let Some(migrate_fn) = path.steps.get(¤t_version) {
417 current_data = migrate_fn(current_data.clone())?;
419
420 match path.versions.iter().position(|v| v == ¤t_version) {
422 Some(idx) if idx + 1 < path.versions.len() => {
423 current_version = path.versions[idx + 1].clone();
424 }
425 _ => break,
426 }
427 }
428
429 let domain_value = (path.finalize)(current_data)?;
431
432 serde_json::from_value(domain_value).map_err(|e| {
433 MigrationError::DeserializationError(format!("Failed to convert to domain: {}", e))
434 })
435 }
436
437 pub fn save<T: Versioned + Serialize>(&self, data: T) -> Result<String, MigrationError> {
468 let version_key = T::VERSION_KEY;
470 let data_key = T::DATA_KEY;
471
472 let data_value = serde_json::to_value(&data).map_err(|e| {
474 MigrationError::SerializationError(format!("Failed to serialize data: {}", e))
475 })?;
476
477 let mut map = serde_json::Map::new();
479 map.insert(
480 version_key.to_string(),
481 serde_json::Value::String(T::VERSION.to_string()),
482 );
483 map.insert(data_key.to_string(), data_value);
484
485 serde_json::to_string(&map).map_err(|e| {
486 MigrationError::SerializationError(format!("Failed to serialize wrapper: {}", e))
487 })
488 }
489
490 pub fn save_flat<T: Versioned + Serialize>(&self, data: T) -> Result<String, MigrationError> {
520 let version_key = T::VERSION_KEY;
521
522 let mut data_value = serde_json::to_value(&data).map_err(|e| {
524 MigrationError::SerializationError(format!("Failed to serialize data: {}", e))
525 })?;
526
527 let obj = data_value.as_object_mut().ok_or_else(|| {
529 MigrationError::SerializationError(
530 "Data must serialize to a JSON object for flat format".to_string(),
531 )
532 })?;
533
534 obj.insert(
536 version_key.to_string(),
537 serde_json::Value::String(T::VERSION.to_string()),
538 );
539
540 serde_json::to_string(&obj).map_err(|e| {
541 MigrationError::SerializationError(format!("Failed to serialize flat format: {}", e))
542 })
543 }
544
545 pub fn load_vec_from<D, T>(&self, entity: &str, data: Vec<T>) -> Result<Vec<D>, MigrationError>
578 where
579 D: DeserializeOwned,
580 T: Serialize,
581 {
582 data.into_iter()
583 .map(|item| self.load_from(entity, item))
584 .collect()
585 }
586
587 pub fn load_vec<D: DeserializeOwned>(
618 &self,
619 entity: &str,
620 json: &str,
621 ) -> Result<Vec<D>, MigrationError> {
622 let data: Vec<serde_json::Value> = serde_json::from_str(json).map_err(|e| {
623 MigrationError::DeserializationError(format!("Failed to parse JSON array: {}", e))
624 })?;
625 self.load_vec_from(entity, data)
626 }
627
628 pub fn load_vec_flat<D: DeserializeOwned>(
659 &self,
660 entity: &str,
661 json: &str,
662 ) -> Result<Vec<D>, MigrationError> {
663 let data: Vec<serde_json::Value> = serde_json::from_str(json).map_err(|e| {
664 MigrationError::DeserializationError(format!("Failed to parse JSON array: {}", e))
665 })?;
666 self.load_vec_flat_from(entity, data)
667 }
668
669 pub fn load_vec_flat_from<D, T>(
698 &self,
699 entity: &str,
700 data: Vec<T>,
701 ) -> Result<Vec<D>, MigrationError>
702 where
703 D: DeserializeOwned,
704 T: Serialize,
705 {
706 data.into_iter()
707 .map(|item| self.load_flat_from(entity, item))
708 .collect()
709 }
710
711 pub fn save_vec<T: Versioned + Serialize>(
748 &self,
749 data: Vec<T>,
750 ) -> Result<String, MigrationError> {
751 let version_key = T::VERSION_KEY;
752 let data_key = T::DATA_KEY;
753
754 let wrappers: Result<Vec<serde_json::Value>, MigrationError> = data
755 .into_iter()
756 .map(|item| {
757 let data_value = serde_json::to_value(&item).map_err(|e| {
758 MigrationError::SerializationError(format!("Failed to serialize item: {}", e))
759 })?;
760
761 let mut map = serde_json::Map::new();
762 map.insert(
763 version_key.to_string(),
764 serde_json::Value::String(T::VERSION.to_string()),
765 );
766 map.insert(data_key.to_string(), data_value);
767
768 Ok(serde_json::Value::Object(map))
769 })
770 .collect();
771
772 serde_json::to_string(&wrappers?).map_err(|e| {
773 MigrationError::SerializationError(format!("Failed to serialize data array: {}", e))
774 })
775 }
776
777 pub fn save_vec_flat<T: Versioned + Serialize>(
813 &self,
814 data: Vec<T>,
815 ) -> Result<String, MigrationError> {
816 let version_key = T::VERSION_KEY;
817
818 let flat_items: Result<Vec<serde_json::Value>, MigrationError> = data
819 .into_iter()
820 .map(|item| {
821 let mut data_value = serde_json::to_value(&item).map_err(|e| {
822 MigrationError::SerializationError(format!("Failed to serialize item: {}", e))
823 })?;
824
825 let obj = data_value.as_object_mut().ok_or_else(|| {
826 MigrationError::SerializationError(
827 "Data must serialize to a JSON object for flat format".to_string(),
828 )
829 })?;
830
831 obj.insert(
833 version_key.to_string(),
834 serde_json::Value::String(T::VERSION.to_string()),
835 );
836
837 Ok(serde_json::Value::Object(obj.clone()))
838 })
839 .collect();
840
841 serde_json::to_string(&flat_items?).map_err(|e| {
842 MigrationError::SerializationError(format!(
843 "Failed to serialize flat data array: {}",
844 e
845 ))
846 })
847 }
848}
849
850impl Default for Migrator {
851 fn default() -> Self {
852 Self::new()
853 }
854}
855
856pub struct MigratorBuilder {
858 default_version_key: Option<String>,
859 default_data_key: Option<String>,
860}
861
862impl MigratorBuilder {
863 pub(crate) fn new() -> Self {
864 Self {
865 default_version_key: None,
866 default_data_key: None,
867 }
868 }
869
870 pub fn default_version_key(mut self, key: impl Into<String>) -> Self {
876 self.default_version_key = Some(key.into());
877 self
878 }
879
880 pub fn default_data_key(mut self, key: impl Into<String>) -> Self {
886 self.default_data_key = Some(key.into());
887 self
888 }
889
890 pub fn build(self) -> Migrator {
892 Migrator {
893 paths: HashMap::new(),
894 default_version_key: self.default_version_key,
895 default_data_key: self.default_data_key,
896 }
897 }
898}
899
900pub struct Start;
902
903pub struct HasFrom<V>(PhantomData<V>);
905
906pub struct HasSteps<V>(PhantomData<V>);
908
909pub struct MigrationPathBuilder<State> {
911 entity: String,
912 steps: HashMap<String, MigrationFn>,
913 versions: Vec<String>,
914 version_key: String,
915 data_key: String,
916 custom_version_key: Option<String>,
917 custom_data_key: Option<String>,
918 _state: PhantomData<State>,
919}
920
921impl MigrationPathBuilder<Start> {
922 fn new(entity: String) -> Self {
923 Self {
924 entity,
925 steps: HashMap::new(),
926 versions: Vec::new(),
927 version_key: String::from("version"),
928 data_key: String::from("data"),
929 custom_version_key: None,
930 custom_data_key: None,
931 _state: PhantomData,
932 }
933 }
934
935 pub fn with_keys(
948 mut self,
949 version_key: impl Into<String>,
950 data_key: impl Into<String>,
951 ) -> Self {
952 self.custom_version_key = Some(version_key.into());
953 self.custom_data_key = Some(data_key.into());
954 self
955 }
956
957 pub fn from<V: Versioned + DeserializeOwned>(self) -> MigrationPathBuilder<HasFrom<V>> {
959 let mut versions = self.versions;
960 versions.push(V::VERSION.to_string());
961
962 MigrationPathBuilder {
963 entity: self.entity,
964 steps: self.steps,
965 versions,
966 version_key: V::VERSION_KEY.to_string(),
967 data_key: V::DATA_KEY.to_string(),
968 custom_version_key: self.custom_version_key,
969 custom_data_key: self.custom_data_key,
970 _state: PhantomData,
971 }
972 }
973}
974
975impl<V> MigrationPathBuilder<HasFrom<V>>
976where
977 V: Versioned + DeserializeOwned,
978{
979 pub fn step<Next>(mut self) -> MigrationPathBuilder<HasSteps<Next>>
981 where
982 V: MigratesTo<Next>,
983 Next: Versioned + DeserializeOwned + Serialize,
984 {
985 let from_version = V::VERSION.to_string();
986 let migration_fn: MigrationFn = Box::new(move |value| {
987 let from_value: V = serde_json::from_value(value).map_err(|e| {
988 MigrationError::DeserializationError(format!(
989 "Failed to deserialize version {}: {}",
990 V::VERSION,
991 e
992 ))
993 })?;
994
995 let to_value = from_value.migrate();
996
997 serde_json::to_value(&to_value).map_err(|e| MigrationError::MigrationStepFailed {
999 from: V::VERSION.to_string(),
1000 to: Next::VERSION.to_string(),
1001 error: e.to_string(),
1002 })
1003 });
1004
1005 self.steps.insert(from_version, migration_fn);
1006 self.versions.push(Next::VERSION.to_string());
1007
1008 MigrationPathBuilder {
1009 entity: self.entity,
1010 steps: self.steps,
1011 versions: self.versions,
1012 version_key: self.version_key,
1013 data_key: self.data_key,
1014 custom_version_key: self.custom_version_key,
1015 custom_data_key: self.custom_data_key,
1016 _state: PhantomData,
1017 }
1018 }
1019
1020 pub fn into<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1022 where
1023 V: IntoDomain<D>,
1024 {
1025 let finalize: Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError>> =
1026 Box::new(move |value| {
1027 let versioned: V = serde_json::from_value(value).map_err(|e| {
1028 MigrationError::DeserializationError(format!(
1029 "Failed to deserialize final version: {}",
1030 e
1031 ))
1032 })?;
1033
1034 let domain = versioned.into_domain();
1035
1036 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1037 from: V::VERSION.to_string(),
1038 to: "domain".to_string(),
1039 error: e.to_string(),
1040 })
1041 });
1042
1043 MigrationPath {
1044 entity: self.entity,
1045 inner: EntityMigrationPath {
1046 steps: self.steps,
1047 finalize,
1048 versions: self.versions.clone(),
1049 version_key: self.version_key,
1050 data_key: self.data_key,
1051 },
1052 versions: self.versions,
1053 custom_version_key: self.custom_version_key,
1054 custom_data_key: self.custom_data_key,
1055 _phantom: PhantomData,
1056 }
1057 }
1058}
1059
1060impl<V> MigrationPathBuilder<HasSteps<V>>
1061where
1062 V: Versioned + DeserializeOwned,
1063{
1064 pub fn step<Next>(mut self) -> MigrationPathBuilder<HasSteps<Next>>
1066 where
1067 V: MigratesTo<Next>,
1068 Next: Versioned + DeserializeOwned + Serialize,
1069 {
1070 let from_version = V::VERSION.to_string();
1071 let migration_fn: MigrationFn = Box::new(move |value| {
1072 let from_value: V = serde_json::from_value(value).map_err(|e| {
1073 MigrationError::DeserializationError(format!(
1074 "Failed to deserialize version {}: {}",
1075 V::VERSION,
1076 e
1077 ))
1078 })?;
1079
1080 let to_value = from_value.migrate();
1081
1082 serde_json::to_value(&to_value).map_err(|e| MigrationError::MigrationStepFailed {
1084 from: V::VERSION.to_string(),
1085 to: Next::VERSION.to_string(),
1086 error: e.to_string(),
1087 })
1088 });
1089
1090 self.steps.insert(from_version, migration_fn);
1091 self.versions.push(Next::VERSION.to_string());
1092
1093 MigrationPathBuilder {
1094 entity: self.entity,
1095 steps: self.steps,
1096 versions: self.versions,
1097 version_key: self.version_key,
1098 data_key: self.data_key,
1099 custom_version_key: self.custom_version_key,
1100 custom_data_key: self.custom_data_key,
1101 _state: PhantomData,
1102 }
1103 }
1104
1105 pub fn into<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1107 where
1108 V: IntoDomain<D>,
1109 {
1110 let finalize: Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError>> =
1111 Box::new(move |value| {
1112 let versioned: V = serde_json::from_value(value).map_err(|e| {
1113 MigrationError::DeserializationError(format!(
1114 "Failed to deserialize final version: {}",
1115 e
1116 ))
1117 })?;
1118
1119 let domain = versioned.into_domain();
1120
1121 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1122 from: V::VERSION.to_string(),
1123 to: "domain".to_string(),
1124 error: e.to_string(),
1125 })
1126 });
1127
1128 MigrationPath {
1129 entity: self.entity,
1130 inner: EntityMigrationPath {
1131 steps: self.steps,
1132 finalize,
1133 versions: self.versions.clone(),
1134 version_key: self.version_key,
1135 data_key: self.data_key,
1136 },
1137 versions: self.versions,
1138 custom_version_key: self.custom_version_key,
1139 custom_data_key: self.custom_data_key,
1140 _phantom: PhantomData,
1141 }
1142 }
1143}
1144
1145pub struct MigrationPath<D> {
1147 entity: String,
1148 inner: EntityMigrationPath,
1149 versions: Vec<String>,
1151 custom_version_key: Option<String>,
1153 custom_data_key: Option<String>,
1155 _phantom: PhantomData<D>,
1156}
1157
1158pub struct ConfigMigrator {
1193 root: serde_json::Value,
1194 migrator: Migrator,
1195}
1196
1197impl ConfigMigrator {
1198 pub fn from(json: &str, migrator: Migrator) -> Result<Self, MigrationError> {
1204 let root = serde_json::from_str(json)
1205 .map_err(|e| MigrationError::DeserializationError(e.to_string()))?;
1206 Ok(Self { root, migrator })
1207 }
1208
1209 pub fn query<T>(&self, key: &str) -> Result<Vec<T>, MigrationError>
1229 where
1230 T: crate::Queryable + for<'de> serde::Deserialize<'de>,
1231 {
1232 let value = &self.root[key];
1233 if value.is_null() {
1234 return Ok(Vec::new());
1235 }
1236
1237 if !value.is_array() {
1238 return Err(MigrationError::DeserializationError(format!(
1239 "Key '{}' does not contain an array",
1240 key
1241 )));
1242 }
1243
1244 let array = value.as_array().unwrap(); self.migrator
1246 .load_vec_flat_from(T::ENTITY_NAME, array.to_vec())
1247 }
1248
1249 pub fn update<T>(&mut self, key: &str, data: Vec<T>) -> Result<(), MigrationError>
1270 where
1271 T: serde::Serialize + crate::Queryable,
1272 {
1273 let entity_name = T::ENTITY_NAME;
1274 let latest_version = self
1275 .migrator
1276 .get_latest_version(entity_name)
1277 .ok_or_else(|| MigrationError::EntityNotFound(entity_name.to_string()))?;
1278
1279 let items: Vec<serde_json::Value> = data
1281 .into_iter()
1282 .map(|item| {
1283 let mut obj = serde_json::to_value(&item)
1284 .map_err(|e| MigrationError::SerializationError(e.to_string()))?;
1285
1286 if let Some(obj_map) = obj.as_object_mut() {
1287 obj_map.insert(
1288 "version".to_string(),
1289 serde_json::Value::String(latest_version.to_string()),
1290 );
1291 }
1292
1293 Ok(obj)
1294 })
1295 .collect::<Result<Vec<_>, MigrationError>>()?;
1296
1297 self.root[key] = serde_json::Value::Array(items);
1298 Ok(())
1299 }
1300
1301 pub fn to_string(&self) -> Result<String, MigrationError> {
1307 serde_json::to_string_pretty(&self.root)
1308 .map_err(|e| MigrationError::SerializationError(e.to_string()))
1309 }
1310
1311 pub fn to_string_compact(&self) -> Result<String, MigrationError> {
1317 serde_json::to_string(&self.root)
1318 .map_err(|e| MigrationError::SerializationError(e.to_string()))
1319 }
1320
1321 pub fn as_value(&self) -> &serde_json::Value {
1323 &self.root
1324 }
1325}
1326
1327#[cfg(test)]
1328mod tests {
1329 use super::*;
1330 use crate::{IntoDomain, MigratesTo, Versioned, VersionedWrapper};
1331 use serde::{Deserialize, Serialize};
1332
1333 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1335 struct V1 {
1336 value: String,
1337 }
1338
1339 impl Versioned for V1 {
1340 const VERSION: &'static str = "1.0.0";
1341 }
1342
1343 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1344 struct V2 {
1345 value: String,
1346 count: u32,
1347 }
1348
1349 impl Versioned for V2 {
1350 const VERSION: &'static str = "2.0.0";
1351 }
1352
1353 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1354 struct V3 {
1355 value: String,
1356 count: u32,
1357 enabled: bool,
1358 }
1359
1360 impl Versioned for V3 {
1361 const VERSION: &'static str = "3.0.0";
1362 }
1363
1364 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1365 struct Domain {
1366 value: String,
1367 count: u32,
1368 enabled: bool,
1369 }
1370
1371 impl MigratesTo<V2> for V1 {
1372 fn migrate(self) -> V2 {
1373 V2 {
1374 value: self.value,
1375 count: 0,
1376 }
1377 }
1378 }
1379
1380 impl MigratesTo<V3> for V2 {
1381 fn migrate(self) -> V3 {
1382 V3 {
1383 value: self.value,
1384 count: self.count,
1385 enabled: true,
1386 }
1387 }
1388 }
1389
1390 impl IntoDomain<Domain> for V3 {
1391 fn into_domain(self) -> Domain {
1392 Domain {
1393 value: self.value,
1394 count: self.count,
1395 enabled: self.enabled,
1396 }
1397 }
1398 }
1399
1400 #[test]
1401 fn test_migrator_new() {
1402 let migrator = Migrator::new();
1403 assert_eq!(migrator.paths.len(), 0);
1404 }
1405
1406 #[test]
1407 fn test_migrator_default() {
1408 let migrator = Migrator::default();
1409 assert_eq!(migrator.paths.len(), 0);
1410 }
1411
1412 #[test]
1413 fn test_single_step_migration() {
1414 let path = Migrator::define("test")
1415 .from::<V2>()
1416 .step::<V3>()
1417 .into::<Domain>();
1418
1419 let mut migrator = Migrator::new();
1420 migrator.register(path).unwrap();
1421
1422 let v2 = V2 {
1423 value: "test".to_string(),
1424 count: 42,
1425 };
1426 let wrapper = VersionedWrapper::from_versioned(v2);
1427 let json = serde_json::to_string(&wrapper).unwrap();
1428
1429 let result: Domain = migrator.load("test", &json).unwrap();
1430 assert_eq!(result.value, "test");
1431 assert_eq!(result.count, 42);
1432 assert!(result.enabled);
1433 }
1434
1435 #[test]
1436 fn test_multi_step_migration() {
1437 let path = Migrator::define("test")
1438 .from::<V1>()
1439 .step::<V2>()
1440 .step::<V3>()
1441 .into::<Domain>();
1442
1443 let mut migrator = Migrator::new();
1444 migrator.register(path).unwrap();
1445
1446 let v1 = V1 {
1447 value: "multi_step".to_string(),
1448 };
1449 let wrapper = VersionedWrapper::from_versioned(v1);
1450 let json = serde_json::to_string(&wrapper).unwrap();
1451
1452 let result: Domain = migrator.load("test", &json).unwrap();
1453 assert_eq!(result.value, "multi_step");
1454 assert_eq!(result.count, 0);
1455 assert!(result.enabled);
1456 }
1457
1458 #[test]
1459 fn test_no_migration_needed() {
1460 let path = Migrator::define("test").from::<V3>().into::<Domain>();
1461
1462 let mut migrator = Migrator::new();
1463 migrator.register(path).unwrap();
1464
1465 let v3 = V3 {
1466 value: "latest".to_string(),
1467 count: 100,
1468 enabled: false,
1469 };
1470 let wrapper = VersionedWrapper::from_versioned(v3);
1471 let json = serde_json::to_string(&wrapper).unwrap();
1472
1473 let result: Domain = migrator.load("test", &json).unwrap();
1474 assert_eq!(result.value, "latest");
1475 assert_eq!(result.count, 100);
1476 assert!(!result.enabled);
1477 }
1478
1479 #[test]
1480 fn test_entity_not_found() {
1481 let migrator = Migrator::new();
1482
1483 let v1 = V1 {
1484 value: "test".to_string(),
1485 };
1486 let wrapper = VersionedWrapper::from_versioned(v1);
1487 let json = serde_json::to_string(&wrapper).unwrap();
1488
1489 let result: Result<Domain, MigrationError> = migrator.load("unknown", &json);
1490 assert!(matches!(result, Err(MigrationError::EntityNotFound(_))));
1491
1492 if let Err(MigrationError::EntityNotFound(entity)) = result {
1493 assert_eq!(entity, "unknown");
1494 }
1495 }
1496
1497 #[test]
1498 fn test_invalid_json() {
1499 let path = Migrator::define("test").from::<V3>().into::<Domain>();
1500
1501 let mut migrator = Migrator::new();
1502 migrator.register(path).unwrap();
1503
1504 let invalid_json = "{ invalid json }";
1505 let result: Result<Domain, MigrationError> = migrator.load("test", invalid_json);
1506
1507 assert!(matches!(
1508 result,
1509 Err(MigrationError::DeserializationError(_))
1510 ));
1511 }
1512
1513 #[test]
1514 fn test_multiple_entities() {
1515 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1516 struct OtherDomain {
1517 value: String,
1518 }
1519
1520 impl IntoDomain<OtherDomain> for V1 {
1521 fn into_domain(self) -> OtherDomain {
1522 OtherDomain { value: self.value }
1523 }
1524 }
1525
1526 let path1 = Migrator::define("entity1")
1527 .from::<V1>()
1528 .step::<V2>()
1529 .step::<V3>()
1530 .into::<Domain>();
1531
1532 let path2 = Migrator::define("entity2")
1533 .from::<V1>()
1534 .into::<OtherDomain>();
1535
1536 let mut migrator = Migrator::new();
1537 migrator.register(path1).unwrap();
1538 migrator.register(path2).unwrap();
1539
1540 let v1 = V1 {
1542 value: "entity1".to_string(),
1543 };
1544 let wrapper = VersionedWrapper::from_versioned(v1);
1545 let json = serde_json::to_string(&wrapper).unwrap();
1546 let result: Domain = migrator.load("entity1", &json).unwrap();
1547 assert_eq!(result.value, "entity1");
1548
1549 let v1 = V1 {
1551 value: "entity2".to_string(),
1552 };
1553 let wrapper = VersionedWrapper::from_versioned(v1);
1554 let json = serde_json::to_string(&wrapper).unwrap();
1555 let result: OtherDomain = migrator.load("entity2", &json).unwrap();
1556 assert_eq!(result.value, "entity2");
1557 }
1558
1559 #[test]
1560 fn test_save() {
1561 let migrator = Migrator::new();
1562
1563 let v1 = V1 {
1564 value: "test_save".to_string(),
1565 };
1566
1567 let json = migrator.save(v1).unwrap();
1568
1569 assert!(json.contains("\"version\""));
1571 assert!(json.contains("\"1.0.0\""));
1572 assert!(json.contains("\"data\""));
1573 assert!(json.contains("\"test_save\""));
1574
1575 let parsed: VersionedWrapper<serde_json::Value> = serde_json::from_str(&json).unwrap();
1577 assert_eq!(parsed.version, "1.0.0");
1578 }
1579
1580 #[test]
1581 fn test_save_and_load_roundtrip() {
1582 let path = Migrator::define("test")
1583 .from::<V1>()
1584 .step::<V2>()
1585 .step::<V3>()
1586 .into::<Domain>();
1587
1588 let mut migrator = Migrator::new();
1589 migrator.register(path).unwrap();
1590
1591 let v1 = V1 {
1593 value: "roundtrip".to_string(),
1594 };
1595 let json = migrator.save(v1).unwrap();
1596
1597 let domain: Domain = migrator.load("test", &json).unwrap();
1599
1600 assert_eq!(domain.value, "roundtrip");
1601 assert_eq!(domain.count, 0); assert!(domain.enabled); }
1604
1605 #[test]
1606 fn test_save_latest_version() {
1607 let migrator = Migrator::new();
1608
1609 let v3 = V3 {
1610 value: "latest".to_string(),
1611 count: 42,
1612 enabled: false,
1613 };
1614
1615 let json = migrator.save(v3).unwrap();
1616
1617 assert!(json.contains("\"version\":\"3.0.0\""));
1619 assert!(json.contains("\"value\":\"latest\""));
1620 assert!(json.contains("\"count\":42"));
1621 assert!(json.contains("\"enabled\":false"));
1622 }
1623
1624 #[test]
1625 fn test_save_pretty() {
1626 let migrator = Migrator::new();
1627
1628 let v2 = V2 {
1629 value: "pretty".to_string(),
1630 count: 10,
1631 };
1632
1633 let json = migrator.save(v2).unwrap();
1634
1635 assert!(!json.contains('\n'));
1637 assert!(json.contains("\"version\":\"2.0.0\""));
1638 }
1639
1640 #[test]
1641 fn test_validation_invalid_version_order() {
1642 let entity = "test".to_string();
1644 let versions = vec!["2.0.0".to_string(), "1.0.0".to_string()]; let result = Migrator::validate_migration_path(&entity, &versions);
1647 assert!(matches!(
1648 result,
1649 Err(MigrationError::InvalidVersionOrder { .. })
1650 ));
1651
1652 if let Err(MigrationError::InvalidVersionOrder {
1653 entity: e,
1654 from,
1655 to,
1656 }) = result
1657 {
1658 assert_eq!(e, "test");
1659 assert_eq!(from, "2.0.0");
1660 assert_eq!(to, "1.0.0");
1661 }
1662 }
1663
1664 #[test]
1665 fn test_validation_circular_path() {
1666 let entity = "test".to_string();
1668 let versions = vec![
1669 "1.0.0".to_string(),
1670 "2.0.0".to_string(),
1671 "1.0.0".to_string(), ];
1673
1674 let result = Migrator::validate_migration_path(&entity, &versions);
1675 assert!(matches!(
1676 result,
1677 Err(MigrationError::CircularMigrationPath { .. })
1678 ));
1679
1680 if let Err(MigrationError::CircularMigrationPath { entity: e, path }) = result {
1681 assert_eq!(e, "test");
1682 assert!(path.contains("1.0.0"));
1683 assert!(path.contains("2.0.0"));
1684 }
1685 }
1686
1687 #[test]
1688 fn test_validation_valid_path() {
1689 let entity = "test".to_string();
1691 let versions = vec![
1692 "1.0.0".to_string(),
1693 "1.1.0".to_string(),
1694 "2.0.0".to_string(),
1695 ];
1696
1697 let result = Migrator::validate_migration_path(&entity, &versions);
1698 assert!(result.is_ok());
1699 }
1700
1701 #[test]
1702 fn test_validation_empty_path() {
1703 let entity = "test".to_string();
1705 let versions = vec![];
1706
1707 let result = Migrator::validate_migration_path(&entity, &versions);
1708 assert!(result.is_ok());
1709 }
1710
1711 #[test]
1712 fn test_validation_single_version() {
1713 let entity = "test".to_string();
1715 let versions = vec!["1.0.0".to_string()];
1716
1717 let result = Migrator::validate_migration_path(&entity, &versions);
1718 assert!(result.is_ok());
1719 }
1720
1721 #[test]
1723 fn test_save_vec_and_load_vec() {
1724 let migrator = Migrator::new();
1725
1726 let items = vec![
1728 V1 {
1729 value: "item1".to_string(),
1730 },
1731 V1 {
1732 value: "item2".to_string(),
1733 },
1734 V1 {
1735 value: "item3".to_string(),
1736 },
1737 ];
1738
1739 let json = migrator.save_vec(items).unwrap();
1740
1741 assert!(json.starts_with('['));
1743 assert!(json.ends_with(']'));
1744 assert!(json.contains("\"version\":\"1.0.0\""));
1745 assert!(json.contains("item1"));
1746 assert!(json.contains("item2"));
1747 assert!(json.contains("item3"));
1748
1749 let path = Migrator::define("test")
1751 .from::<V1>()
1752 .step::<V2>()
1753 .step::<V3>()
1754 .into::<Domain>();
1755
1756 let mut migrator = Migrator::new();
1757 migrator.register(path).unwrap();
1758
1759 let domains: Vec<Domain> = migrator.load_vec("test", &json).unwrap();
1761
1762 assert_eq!(domains.len(), 3);
1763 assert_eq!(domains[0].value, "item1");
1764 assert_eq!(domains[1].value, "item2");
1765 assert_eq!(domains[2].value, "item3");
1766
1767 for domain in &domains {
1769 assert_eq!(domain.count, 0);
1770 assert!(domain.enabled);
1771 }
1772 }
1773
1774 #[test]
1775 fn test_load_vec_empty_array() {
1776 let path = Migrator::define("test")
1777 .from::<V1>()
1778 .step::<V2>()
1779 .step::<V3>()
1780 .into::<Domain>();
1781
1782 let mut migrator = Migrator::new();
1783 migrator.register(path).unwrap();
1784
1785 let json = "[]";
1786 let domains: Vec<Domain> = migrator.load_vec("test", json).unwrap();
1787
1788 assert_eq!(domains.len(), 0);
1789 }
1790
1791 #[test]
1792 fn test_load_vec_mixed_versions() {
1793 let path = Migrator::define("test")
1795 .from::<V1>()
1796 .step::<V2>()
1797 .step::<V3>()
1798 .into::<Domain>();
1799
1800 let mut migrator = Migrator::new();
1801 migrator.register(path).unwrap();
1802
1803 let json = r#"[
1805 {"version":"1.0.0","data":{"value":"v1-item"}},
1806 {"version":"2.0.0","data":{"value":"v2-item","count":42}},
1807 {"version":"3.0.0","data":{"value":"v3-item","count":99,"enabled":false}}
1808 ]"#;
1809
1810 let domains: Vec<Domain> = migrator.load_vec("test", json).unwrap();
1811
1812 assert_eq!(domains.len(), 3);
1813
1814 assert_eq!(domains[0].value, "v1-item");
1816 assert_eq!(domains[0].count, 0);
1817 assert!(domains[0].enabled);
1818
1819 assert_eq!(domains[1].value, "v2-item");
1821 assert_eq!(domains[1].count, 42);
1822 assert!(domains[1].enabled);
1823
1824 assert_eq!(domains[2].value, "v3-item");
1826 assert_eq!(domains[2].count, 99);
1827 assert!(!domains[2].enabled);
1828 }
1829
1830 #[test]
1831 fn test_load_vec_from_json_values() {
1832 let path = Migrator::define("test")
1833 .from::<V1>()
1834 .step::<V2>()
1835 .step::<V3>()
1836 .into::<Domain>();
1837
1838 let mut migrator = Migrator::new();
1839 migrator.register(path).unwrap();
1840
1841 let values: Vec<serde_json::Value> = vec![
1843 serde_json::json!({"version":"1.0.0","data":{"value":"direct1"}}),
1844 serde_json::json!({"version":"1.0.0","data":{"value":"direct2"}}),
1845 ];
1846
1847 let domains: Vec<Domain> = migrator.load_vec_from("test", values).unwrap();
1848
1849 assert_eq!(domains.len(), 2);
1850 assert_eq!(domains[0].value, "direct1");
1851 assert_eq!(domains[1].value, "direct2");
1852 }
1853
1854 #[test]
1855 fn test_save_vec_empty() {
1856 let migrator = Migrator::new();
1857 let empty: Vec<V1> = vec![];
1858
1859 let json = migrator.save_vec(empty).unwrap();
1860
1861 assert_eq!(json, "[]");
1862 }
1863
1864 #[test]
1865 fn test_load_vec_invalid_json() {
1866 let path = Migrator::define("test")
1867 .from::<V1>()
1868 .step::<V2>()
1869 .step::<V3>()
1870 .into::<Domain>();
1871
1872 let mut migrator = Migrator::new();
1873 migrator.register(path).unwrap();
1874
1875 let invalid_json = "{ not an array }";
1876 let result: Result<Vec<Domain>, MigrationError> = migrator.load_vec("test", invalid_json);
1877
1878 assert!(matches!(
1879 result,
1880 Err(MigrationError::DeserializationError(_))
1881 ));
1882 }
1883
1884 #[test]
1885 fn test_load_vec_entity_not_found() {
1886 let migrator = Migrator::new();
1887
1888 let json = r#"[{"version":"1.0.0","data":{"value":"test"}}]"#;
1889 let result: Result<Vec<Domain>, MigrationError> = migrator.load_vec("unknown", json);
1890
1891 assert!(matches!(result, Err(MigrationError::EntityNotFound(_))));
1892 }
1893
1894 #[test]
1895 fn test_save_vec_latest_version() {
1896 let migrator = Migrator::new();
1897
1898 let items = vec![
1899 V3 {
1900 value: "latest1".to_string(),
1901 count: 10,
1902 enabled: true,
1903 },
1904 V3 {
1905 value: "latest2".to_string(),
1906 count: 20,
1907 enabled: false,
1908 },
1909 ];
1910
1911 let json = migrator.save_vec(items).unwrap();
1912
1913 assert!(json.contains("\"version\":\"3.0.0\""));
1915 assert!(json.contains("latest1"));
1916 assert!(json.contains("latest2"));
1917 assert!(json.contains("\"count\":10"));
1918 assert!(json.contains("\"count\":20"));
1919 }
1920}