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 pub fn save_entity<E: crate::LatestVersioned>(
887 &self,
888 entity: E,
889 ) -> Result<String, MigrationError> {
890 let latest = entity.to_latest();
891 self.save(latest)
892 }
893
894 pub fn save_entity_flat<E: crate::LatestVersioned>(
932 &self,
933 entity: E,
934 ) -> Result<String, MigrationError> {
935 let latest = entity.to_latest();
936 self.save_flat(latest)
937 }
938
939 pub fn save_entity_vec<E: crate::LatestVersioned>(
967 &self,
968 entities: Vec<E>,
969 ) -> Result<String, MigrationError> {
970 let versioned: Vec<E::Latest> = entities.into_iter().map(|e| e.to_latest()).collect();
971 self.save_vec(versioned)
972 }
973
974 pub fn save_entity_vec_flat<E: crate::LatestVersioned>(
1002 &self,
1003 entities: Vec<E>,
1004 ) -> Result<String, MigrationError> {
1005 let versioned: Vec<E::Latest> = entities.into_iter().map(|e| e.to_latest()).collect();
1006 self.save_vec_flat(versioned)
1007 }
1008}
1009
1010impl Default for Migrator {
1011 fn default() -> Self {
1012 Self::new()
1013 }
1014}
1015
1016pub struct MigratorBuilder {
1018 default_version_key: Option<String>,
1019 default_data_key: Option<String>,
1020}
1021
1022impl MigratorBuilder {
1023 pub(crate) fn new() -> Self {
1024 Self {
1025 default_version_key: None,
1026 default_data_key: None,
1027 }
1028 }
1029
1030 pub fn default_version_key(mut self, key: impl Into<String>) -> Self {
1036 self.default_version_key = Some(key.into());
1037 self
1038 }
1039
1040 pub fn default_data_key(mut self, key: impl Into<String>) -> Self {
1046 self.default_data_key = Some(key.into());
1047 self
1048 }
1049
1050 pub fn build(self) -> Migrator {
1052 Migrator {
1053 paths: HashMap::new(),
1054 default_version_key: self.default_version_key,
1055 default_data_key: self.default_data_key,
1056 }
1057 }
1058}
1059
1060pub struct Start;
1062
1063pub struct HasFrom<V>(PhantomData<V>);
1065
1066pub struct HasSteps<V>(PhantomData<V>);
1068
1069pub struct MigrationPathBuilder<State> {
1071 entity: String,
1072 steps: HashMap<String, MigrationFn>,
1073 versions: Vec<String>,
1074 version_key: String,
1075 data_key: String,
1076 custom_version_key: Option<String>,
1077 custom_data_key: Option<String>,
1078 _state: PhantomData<State>,
1079}
1080
1081impl MigrationPathBuilder<Start> {
1082 fn new(entity: String) -> Self {
1083 Self {
1084 entity,
1085 steps: HashMap::new(),
1086 versions: Vec::new(),
1087 version_key: String::from("version"),
1088 data_key: String::from("data"),
1089 custom_version_key: None,
1090 custom_data_key: None,
1091 _state: PhantomData,
1092 }
1093 }
1094
1095 pub fn with_keys(
1108 mut self,
1109 version_key: impl Into<String>,
1110 data_key: impl Into<String>,
1111 ) -> Self {
1112 self.custom_version_key = Some(version_key.into());
1113 self.custom_data_key = Some(data_key.into());
1114 self
1115 }
1116
1117 pub fn from<V: Versioned + DeserializeOwned>(self) -> MigrationPathBuilder<HasFrom<V>> {
1119 let mut versions = self.versions;
1120 versions.push(V::VERSION.to_string());
1121
1122 MigrationPathBuilder {
1123 entity: self.entity,
1124 steps: self.steps,
1125 versions,
1126 version_key: V::VERSION_KEY.to_string(),
1127 data_key: V::DATA_KEY.to_string(),
1128 custom_version_key: self.custom_version_key,
1129 custom_data_key: self.custom_data_key,
1130 _state: PhantomData,
1131 }
1132 }
1133}
1134
1135impl<V> MigrationPathBuilder<HasFrom<V>>
1136where
1137 V: Versioned + DeserializeOwned,
1138{
1139 pub fn step<Next>(mut self) -> MigrationPathBuilder<HasSteps<Next>>
1141 where
1142 V: MigratesTo<Next>,
1143 Next: Versioned + DeserializeOwned + Serialize,
1144 {
1145 let from_version = V::VERSION.to_string();
1146 let migration_fn: MigrationFn = Box::new(move |value| {
1147 let from_value: V = serde_json::from_value(value).map_err(|e| {
1148 MigrationError::DeserializationError(format!(
1149 "Failed to deserialize version {}: {}",
1150 V::VERSION,
1151 e
1152 ))
1153 })?;
1154
1155 let to_value = from_value.migrate();
1156
1157 serde_json::to_value(&to_value).map_err(|e| MigrationError::MigrationStepFailed {
1159 from: V::VERSION.to_string(),
1160 to: Next::VERSION.to_string(),
1161 error: e.to_string(),
1162 })
1163 });
1164
1165 self.steps.insert(from_version, migration_fn);
1166 self.versions.push(Next::VERSION.to_string());
1167
1168 MigrationPathBuilder {
1169 entity: self.entity,
1170 steps: self.steps,
1171 versions: self.versions,
1172 version_key: self.version_key,
1173 data_key: self.data_key,
1174 custom_version_key: self.custom_version_key,
1175 custom_data_key: self.custom_data_key,
1176 _state: PhantomData,
1177 }
1178 }
1179
1180 pub fn into<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1182 where
1183 V: IntoDomain<D>,
1184 {
1185 let finalize: Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError>> =
1186 Box::new(move |value| {
1187 let versioned: V = serde_json::from_value(value).map_err(|e| {
1188 MigrationError::DeserializationError(format!(
1189 "Failed to deserialize final version: {}",
1190 e
1191 ))
1192 })?;
1193
1194 let domain = versioned.into_domain();
1195
1196 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1197 from: V::VERSION.to_string(),
1198 to: "domain".to_string(),
1199 error: e.to_string(),
1200 })
1201 });
1202
1203 MigrationPath {
1204 entity: self.entity,
1205 inner: EntityMigrationPath {
1206 steps: self.steps,
1207 finalize,
1208 versions: self.versions.clone(),
1209 version_key: self.version_key,
1210 data_key: self.data_key,
1211 },
1212 versions: self.versions,
1213 custom_version_key: self.custom_version_key,
1214 custom_data_key: self.custom_data_key,
1215 _phantom: PhantomData,
1216 }
1217 }
1218}
1219
1220impl<V> MigrationPathBuilder<HasSteps<V>>
1221where
1222 V: Versioned + DeserializeOwned,
1223{
1224 pub fn step<Next>(mut self) -> MigrationPathBuilder<HasSteps<Next>>
1226 where
1227 V: MigratesTo<Next>,
1228 Next: Versioned + DeserializeOwned + Serialize,
1229 {
1230 let from_version = V::VERSION.to_string();
1231 let migration_fn: MigrationFn = Box::new(move |value| {
1232 let from_value: V = serde_json::from_value(value).map_err(|e| {
1233 MigrationError::DeserializationError(format!(
1234 "Failed to deserialize version {}: {}",
1235 V::VERSION,
1236 e
1237 ))
1238 })?;
1239
1240 let to_value = from_value.migrate();
1241
1242 serde_json::to_value(&to_value).map_err(|e| MigrationError::MigrationStepFailed {
1244 from: V::VERSION.to_string(),
1245 to: Next::VERSION.to_string(),
1246 error: e.to_string(),
1247 })
1248 });
1249
1250 self.steps.insert(from_version, migration_fn);
1251 self.versions.push(Next::VERSION.to_string());
1252
1253 MigrationPathBuilder {
1254 entity: self.entity,
1255 steps: self.steps,
1256 versions: self.versions,
1257 version_key: self.version_key,
1258 data_key: self.data_key,
1259 custom_version_key: self.custom_version_key,
1260 custom_data_key: self.custom_data_key,
1261 _state: PhantomData,
1262 }
1263 }
1264
1265 pub fn into<D: DeserializeOwned + Serialize>(self) -> MigrationPath<D>
1267 where
1268 V: IntoDomain<D>,
1269 {
1270 let finalize: Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError>> =
1271 Box::new(move |value| {
1272 let versioned: V = serde_json::from_value(value).map_err(|e| {
1273 MigrationError::DeserializationError(format!(
1274 "Failed to deserialize final version: {}",
1275 e
1276 ))
1277 })?;
1278
1279 let domain = versioned.into_domain();
1280
1281 serde_json::to_value(domain).map_err(|e| MigrationError::MigrationStepFailed {
1282 from: V::VERSION.to_string(),
1283 to: "domain".to_string(),
1284 error: e.to_string(),
1285 })
1286 });
1287
1288 MigrationPath {
1289 entity: self.entity,
1290 inner: EntityMigrationPath {
1291 steps: self.steps,
1292 finalize,
1293 versions: self.versions.clone(),
1294 version_key: self.version_key,
1295 data_key: self.data_key,
1296 },
1297 versions: self.versions,
1298 custom_version_key: self.custom_version_key,
1299 custom_data_key: self.custom_data_key,
1300 _phantom: PhantomData,
1301 }
1302 }
1303}
1304
1305pub struct MigrationPath<D> {
1307 entity: String,
1308 inner: EntityMigrationPath,
1309 versions: Vec<String>,
1311 custom_version_key: Option<String>,
1313 custom_data_key: Option<String>,
1315 _phantom: PhantomData<D>,
1316}
1317
1318pub struct ConfigMigrator {
1353 root: serde_json::Value,
1354 migrator: Migrator,
1355}
1356
1357impl ConfigMigrator {
1358 pub fn from(json: &str, migrator: Migrator) -> Result<Self, MigrationError> {
1364 let root = serde_json::from_str(json)
1365 .map_err(|e| MigrationError::DeserializationError(e.to_string()))?;
1366 Ok(Self { root, migrator })
1367 }
1368
1369 pub fn query<T>(&self, key: &str) -> Result<Vec<T>, MigrationError>
1389 where
1390 T: crate::Queryable + for<'de> serde::Deserialize<'de>,
1391 {
1392 let value = &self.root[key];
1393 if value.is_null() {
1394 return Ok(Vec::new());
1395 }
1396
1397 if !value.is_array() {
1398 return Err(MigrationError::DeserializationError(format!(
1399 "Key '{}' does not contain an array",
1400 key
1401 )));
1402 }
1403
1404 let array = value.as_array().unwrap(); self.migrator
1406 .load_vec_flat_from(T::ENTITY_NAME, array.to_vec())
1407 }
1408
1409 pub fn update<T>(&mut self, key: &str, data: Vec<T>) -> Result<(), MigrationError>
1430 where
1431 T: serde::Serialize + crate::Queryable,
1432 {
1433 let entity_name = T::ENTITY_NAME;
1434 let latest_version = self
1435 .migrator
1436 .get_latest_version(entity_name)
1437 .ok_or_else(|| MigrationError::EntityNotFound(entity_name.to_string()))?;
1438
1439 let items: Vec<serde_json::Value> = data
1441 .into_iter()
1442 .map(|item| {
1443 let mut obj = serde_json::to_value(&item)
1444 .map_err(|e| MigrationError::SerializationError(e.to_string()))?;
1445
1446 if let Some(obj_map) = obj.as_object_mut() {
1447 obj_map.insert(
1448 "version".to_string(),
1449 serde_json::Value::String(latest_version.to_string()),
1450 );
1451 }
1452
1453 Ok(obj)
1454 })
1455 .collect::<Result<Vec<_>, MigrationError>>()?;
1456
1457 self.root[key] = serde_json::Value::Array(items);
1458 Ok(())
1459 }
1460
1461 pub fn to_string(&self) -> Result<String, MigrationError> {
1467 serde_json::to_string_pretty(&self.root)
1468 .map_err(|e| MigrationError::SerializationError(e.to_string()))
1469 }
1470
1471 pub fn to_string_compact(&self) -> Result<String, MigrationError> {
1477 serde_json::to_string(&self.root)
1478 .map_err(|e| MigrationError::SerializationError(e.to_string()))
1479 }
1480
1481 pub fn as_value(&self) -> &serde_json::Value {
1483 &self.root
1484 }
1485}
1486
1487#[cfg(test)]
1488mod tests {
1489 use super::*;
1490 use crate::{IntoDomain, MigratesTo, Versioned, VersionedWrapper};
1491 use serde::{Deserialize, Serialize};
1492
1493 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1495 struct V1 {
1496 value: String,
1497 }
1498
1499 impl Versioned for V1 {
1500 const VERSION: &'static str = "1.0.0";
1501 }
1502
1503 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1504 struct V2 {
1505 value: String,
1506 count: u32,
1507 }
1508
1509 impl Versioned for V2 {
1510 const VERSION: &'static str = "2.0.0";
1511 }
1512
1513 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1514 struct V3 {
1515 value: String,
1516 count: u32,
1517 enabled: bool,
1518 }
1519
1520 impl Versioned for V3 {
1521 const VERSION: &'static str = "3.0.0";
1522 }
1523
1524 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1525 struct Domain {
1526 value: String,
1527 count: u32,
1528 enabled: bool,
1529 }
1530
1531 impl MigratesTo<V2> for V1 {
1532 fn migrate(self) -> V2 {
1533 V2 {
1534 value: self.value,
1535 count: 0,
1536 }
1537 }
1538 }
1539
1540 impl MigratesTo<V3> for V2 {
1541 fn migrate(self) -> V3 {
1542 V3 {
1543 value: self.value,
1544 count: self.count,
1545 enabled: true,
1546 }
1547 }
1548 }
1549
1550 impl IntoDomain<Domain> for V3 {
1551 fn into_domain(self) -> Domain {
1552 Domain {
1553 value: self.value,
1554 count: self.count,
1555 enabled: self.enabled,
1556 }
1557 }
1558 }
1559
1560 #[test]
1561 fn test_migrator_new() {
1562 let migrator = Migrator::new();
1563 assert_eq!(migrator.paths.len(), 0);
1564 }
1565
1566 #[test]
1567 fn test_migrator_default() {
1568 let migrator = Migrator::default();
1569 assert_eq!(migrator.paths.len(), 0);
1570 }
1571
1572 #[test]
1573 fn test_single_step_migration() {
1574 let path = Migrator::define("test")
1575 .from::<V2>()
1576 .step::<V3>()
1577 .into::<Domain>();
1578
1579 let mut migrator = Migrator::new();
1580 migrator.register(path).unwrap();
1581
1582 let v2 = V2 {
1583 value: "test".to_string(),
1584 count: 42,
1585 };
1586 let wrapper = VersionedWrapper::from_versioned(v2);
1587 let json = serde_json::to_string(&wrapper).unwrap();
1588
1589 let result: Domain = migrator.load("test", &json).unwrap();
1590 assert_eq!(result.value, "test");
1591 assert_eq!(result.count, 42);
1592 assert!(result.enabled);
1593 }
1594
1595 #[test]
1596 fn test_multi_step_migration() {
1597 let path = Migrator::define("test")
1598 .from::<V1>()
1599 .step::<V2>()
1600 .step::<V3>()
1601 .into::<Domain>();
1602
1603 let mut migrator = Migrator::new();
1604 migrator.register(path).unwrap();
1605
1606 let v1 = V1 {
1607 value: "multi_step".to_string(),
1608 };
1609 let wrapper = VersionedWrapper::from_versioned(v1);
1610 let json = serde_json::to_string(&wrapper).unwrap();
1611
1612 let result: Domain = migrator.load("test", &json).unwrap();
1613 assert_eq!(result.value, "multi_step");
1614 assert_eq!(result.count, 0);
1615 assert!(result.enabled);
1616 }
1617
1618 #[test]
1619 fn test_no_migration_needed() {
1620 let path = Migrator::define("test").from::<V3>().into::<Domain>();
1621
1622 let mut migrator = Migrator::new();
1623 migrator.register(path).unwrap();
1624
1625 let v3 = V3 {
1626 value: "latest".to_string(),
1627 count: 100,
1628 enabled: false,
1629 };
1630 let wrapper = VersionedWrapper::from_versioned(v3);
1631 let json = serde_json::to_string(&wrapper).unwrap();
1632
1633 let result: Domain = migrator.load("test", &json).unwrap();
1634 assert_eq!(result.value, "latest");
1635 assert_eq!(result.count, 100);
1636 assert!(!result.enabled);
1637 }
1638
1639 #[test]
1640 fn test_entity_not_found() {
1641 let migrator = Migrator::new();
1642
1643 let v1 = V1 {
1644 value: "test".to_string(),
1645 };
1646 let wrapper = VersionedWrapper::from_versioned(v1);
1647 let json = serde_json::to_string(&wrapper).unwrap();
1648
1649 let result: Result<Domain, MigrationError> = migrator.load("unknown", &json);
1650 assert!(matches!(result, Err(MigrationError::EntityNotFound(_))));
1651
1652 if let Err(MigrationError::EntityNotFound(entity)) = result {
1653 assert_eq!(entity, "unknown");
1654 }
1655 }
1656
1657 #[test]
1658 fn test_invalid_json() {
1659 let path = Migrator::define("test").from::<V3>().into::<Domain>();
1660
1661 let mut migrator = Migrator::new();
1662 migrator.register(path).unwrap();
1663
1664 let invalid_json = "{ invalid json }";
1665 let result: Result<Domain, MigrationError> = migrator.load("test", invalid_json);
1666
1667 assert!(matches!(
1668 result,
1669 Err(MigrationError::DeserializationError(_))
1670 ));
1671 }
1672
1673 #[test]
1674 fn test_multiple_entities() {
1675 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1676 struct OtherDomain {
1677 value: String,
1678 }
1679
1680 impl IntoDomain<OtherDomain> for V1 {
1681 fn into_domain(self) -> OtherDomain {
1682 OtherDomain { value: self.value }
1683 }
1684 }
1685
1686 let path1 = Migrator::define("entity1")
1687 .from::<V1>()
1688 .step::<V2>()
1689 .step::<V3>()
1690 .into::<Domain>();
1691
1692 let path2 = Migrator::define("entity2")
1693 .from::<V1>()
1694 .into::<OtherDomain>();
1695
1696 let mut migrator = Migrator::new();
1697 migrator.register(path1).unwrap();
1698 migrator.register(path2).unwrap();
1699
1700 let v1 = V1 {
1702 value: "entity1".to_string(),
1703 };
1704 let wrapper = VersionedWrapper::from_versioned(v1);
1705 let json = serde_json::to_string(&wrapper).unwrap();
1706 let result: Domain = migrator.load("entity1", &json).unwrap();
1707 assert_eq!(result.value, "entity1");
1708
1709 let v1 = V1 {
1711 value: "entity2".to_string(),
1712 };
1713 let wrapper = VersionedWrapper::from_versioned(v1);
1714 let json = serde_json::to_string(&wrapper).unwrap();
1715 let result: OtherDomain = migrator.load("entity2", &json).unwrap();
1716 assert_eq!(result.value, "entity2");
1717 }
1718
1719 #[test]
1720 fn test_save() {
1721 let migrator = Migrator::new();
1722
1723 let v1 = V1 {
1724 value: "test_save".to_string(),
1725 };
1726
1727 let json = migrator.save(v1).unwrap();
1728
1729 assert!(json.contains("\"version\""));
1731 assert!(json.contains("\"1.0.0\""));
1732 assert!(json.contains("\"data\""));
1733 assert!(json.contains("\"test_save\""));
1734
1735 let parsed: VersionedWrapper<serde_json::Value> = serde_json::from_str(&json).unwrap();
1737 assert_eq!(parsed.version, "1.0.0");
1738 }
1739
1740 #[test]
1741 fn test_save_and_load_roundtrip() {
1742 let path = Migrator::define("test")
1743 .from::<V1>()
1744 .step::<V2>()
1745 .step::<V3>()
1746 .into::<Domain>();
1747
1748 let mut migrator = Migrator::new();
1749 migrator.register(path).unwrap();
1750
1751 let v1 = V1 {
1753 value: "roundtrip".to_string(),
1754 };
1755 let json = migrator.save(v1).unwrap();
1756
1757 let domain: Domain = migrator.load("test", &json).unwrap();
1759
1760 assert_eq!(domain.value, "roundtrip");
1761 assert_eq!(domain.count, 0); assert!(domain.enabled); }
1764
1765 #[test]
1766 fn test_save_latest_version() {
1767 let migrator = Migrator::new();
1768
1769 let v3 = V3 {
1770 value: "latest".to_string(),
1771 count: 42,
1772 enabled: false,
1773 };
1774
1775 let json = migrator.save(v3).unwrap();
1776
1777 assert!(json.contains("\"version\":\"3.0.0\""));
1779 assert!(json.contains("\"value\":\"latest\""));
1780 assert!(json.contains("\"count\":42"));
1781 assert!(json.contains("\"enabled\":false"));
1782 }
1783
1784 #[test]
1785 fn test_save_pretty() {
1786 let migrator = Migrator::new();
1787
1788 let v2 = V2 {
1789 value: "pretty".to_string(),
1790 count: 10,
1791 };
1792
1793 let json = migrator.save(v2).unwrap();
1794
1795 assert!(!json.contains('\n'));
1797 assert!(json.contains("\"version\":\"2.0.0\""));
1798 }
1799
1800 #[test]
1801 fn test_validation_invalid_version_order() {
1802 let entity = "test".to_string();
1804 let versions = vec!["2.0.0".to_string(), "1.0.0".to_string()]; let result = Migrator::validate_migration_path(&entity, &versions);
1807 assert!(matches!(
1808 result,
1809 Err(MigrationError::InvalidVersionOrder { .. })
1810 ));
1811
1812 if let Err(MigrationError::InvalidVersionOrder {
1813 entity: e,
1814 from,
1815 to,
1816 }) = result
1817 {
1818 assert_eq!(e, "test");
1819 assert_eq!(from, "2.0.0");
1820 assert_eq!(to, "1.0.0");
1821 }
1822 }
1823
1824 #[test]
1825 fn test_validation_circular_path() {
1826 let entity = "test".to_string();
1828 let versions = vec![
1829 "1.0.0".to_string(),
1830 "2.0.0".to_string(),
1831 "1.0.0".to_string(), ];
1833
1834 let result = Migrator::validate_migration_path(&entity, &versions);
1835 assert!(matches!(
1836 result,
1837 Err(MigrationError::CircularMigrationPath { .. })
1838 ));
1839
1840 if let Err(MigrationError::CircularMigrationPath { entity: e, path }) = result {
1841 assert_eq!(e, "test");
1842 assert!(path.contains("1.0.0"));
1843 assert!(path.contains("2.0.0"));
1844 }
1845 }
1846
1847 #[test]
1848 fn test_validation_valid_path() {
1849 let entity = "test".to_string();
1851 let versions = vec![
1852 "1.0.0".to_string(),
1853 "1.1.0".to_string(),
1854 "2.0.0".to_string(),
1855 ];
1856
1857 let result = Migrator::validate_migration_path(&entity, &versions);
1858 assert!(result.is_ok());
1859 }
1860
1861 #[test]
1862 fn test_validation_empty_path() {
1863 let entity = "test".to_string();
1865 let versions = vec![];
1866
1867 let result = Migrator::validate_migration_path(&entity, &versions);
1868 assert!(result.is_ok());
1869 }
1870
1871 #[test]
1872 fn test_validation_single_version() {
1873 let entity = "test".to_string();
1875 let versions = vec!["1.0.0".to_string()];
1876
1877 let result = Migrator::validate_migration_path(&entity, &versions);
1878 assert!(result.is_ok());
1879 }
1880
1881 #[test]
1883 fn test_save_vec_and_load_vec() {
1884 let migrator = Migrator::new();
1885
1886 let items = vec![
1888 V1 {
1889 value: "item1".to_string(),
1890 },
1891 V1 {
1892 value: "item2".to_string(),
1893 },
1894 V1 {
1895 value: "item3".to_string(),
1896 },
1897 ];
1898
1899 let json = migrator.save_vec(items).unwrap();
1900
1901 assert!(json.starts_with('['));
1903 assert!(json.ends_with(']'));
1904 assert!(json.contains("\"version\":\"1.0.0\""));
1905 assert!(json.contains("item1"));
1906 assert!(json.contains("item2"));
1907 assert!(json.contains("item3"));
1908
1909 let path = Migrator::define("test")
1911 .from::<V1>()
1912 .step::<V2>()
1913 .step::<V3>()
1914 .into::<Domain>();
1915
1916 let mut migrator = Migrator::new();
1917 migrator.register(path).unwrap();
1918
1919 let domains: Vec<Domain> = migrator.load_vec("test", &json).unwrap();
1921
1922 assert_eq!(domains.len(), 3);
1923 assert_eq!(domains[0].value, "item1");
1924 assert_eq!(domains[1].value, "item2");
1925 assert_eq!(domains[2].value, "item3");
1926
1927 for domain in &domains {
1929 assert_eq!(domain.count, 0);
1930 assert!(domain.enabled);
1931 }
1932 }
1933
1934 #[test]
1935 fn test_load_vec_empty_array() {
1936 let path = Migrator::define("test")
1937 .from::<V1>()
1938 .step::<V2>()
1939 .step::<V3>()
1940 .into::<Domain>();
1941
1942 let mut migrator = Migrator::new();
1943 migrator.register(path).unwrap();
1944
1945 let json = "[]";
1946 let domains: Vec<Domain> = migrator.load_vec("test", json).unwrap();
1947
1948 assert_eq!(domains.len(), 0);
1949 }
1950
1951 #[test]
1952 fn test_load_vec_mixed_versions() {
1953 let path = Migrator::define("test")
1955 .from::<V1>()
1956 .step::<V2>()
1957 .step::<V3>()
1958 .into::<Domain>();
1959
1960 let mut migrator = Migrator::new();
1961 migrator.register(path).unwrap();
1962
1963 let json = r#"[
1965 {"version":"1.0.0","data":{"value":"v1-item"}},
1966 {"version":"2.0.0","data":{"value":"v2-item","count":42}},
1967 {"version":"3.0.0","data":{"value":"v3-item","count":99,"enabled":false}}
1968 ]"#;
1969
1970 let domains: Vec<Domain> = migrator.load_vec("test", json).unwrap();
1971
1972 assert_eq!(domains.len(), 3);
1973
1974 assert_eq!(domains[0].value, "v1-item");
1976 assert_eq!(domains[0].count, 0);
1977 assert!(domains[0].enabled);
1978
1979 assert_eq!(domains[1].value, "v2-item");
1981 assert_eq!(domains[1].count, 42);
1982 assert!(domains[1].enabled);
1983
1984 assert_eq!(domains[2].value, "v3-item");
1986 assert_eq!(domains[2].count, 99);
1987 assert!(!domains[2].enabled);
1988 }
1989
1990 #[test]
1991 fn test_load_vec_from_json_values() {
1992 let path = Migrator::define("test")
1993 .from::<V1>()
1994 .step::<V2>()
1995 .step::<V3>()
1996 .into::<Domain>();
1997
1998 let mut migrator = Migrator::new();
1999 migrator.register(path).unwrap();
2000
2001 let values: Vec<serde_json::Value> = vec![
2003 serde_json::json!({"version":"1.0.0","data":{"value":"direct1"}}),
2004 serde_json::json!({"version":"1.0.0","data":{"value":"direct2"}}),
2005 ];
2006
2007 let domains: Vec<Domain> = migrator.load_vec_from("test", values).unwrap();
2008
2009 assert_eq!(domains.len(), 2);
2010 assert_eq!(domains[0].value, "direct1");
2011 assert_eq!(domains[1].value, "direct2");
2012 }
2013
2014 #[test]
2015 fn test_save_vec_empty() {
2016 let migrator = Migrator::new();
2017 let empty: Vec<V1> = vec![];
2018
2019 let json = migrator.save_vec(empty).unwrap();
2020
2021 assert_eq!(json, "[]");
2022 }
2023
2024 #[test]
2025 fn test_load_vec_invalid_json() {
2026 let path = Migrator::define("test")
2027 .from::<V1>()
2028 .step::<V2>()
2029 .step::<V3>()
2030 .into::<Domain>();
2031
2032 let mut migrator = Migrator::new();
2033 migrator.register(path).unwrap();
2034
2035 let invalid_json = "{ not an array }";
2036 let result: Result<Vec<Domain>, MigrationError> = migrator.load_vec("test", invalid_json);
2037
2038 assert!(matches!(
2039 result,
2040 Err(MigrationError::DeserializationError(_))
2041 ));
2042 }
2043
2044 #[test]
2045 fn test_load_vec_entity_not_found() {
2046 let migrator = Migrator::new();
2047
2048 let json = r#"[{"version":"1.0.0","data":{"value":"test"}}]"#;
2049 let result: Result<Vec<Domain>, MigrationError> = migrator.load_vec("unknown", json);
2050
2051 assert!(matches!(result, Err(MigrationError::EntityNotFound(_))));
2052 }
2053
2054 #[test]
2055 fn test_save_vec_latest_version() {
2056 let migrator = Migrator::new();
2057
2058 let items = vec![
2059 V3 {
2060 value: "latest1".to_string(),
2061 count: 10,
2062 enabled: true,
2063 },
2064 V3 {
2065 value: "latest2".to_string(),
2066 count: 20,
2067 enabled: false,
2068 },
2069 ];
2070
2071 let json = migrator.save_vec(items).unwrap();
2072
2073 assert!(json.contains("\"version\":\"3.0.0\""));
2075 assert!(json.contains("latest1"));
2076 assert!(json.contains("latest2"));
2077 assert!(json.contains("\"count\":10"));
2078 assert!(json.contains("\"count\":20"));
2079 }
2080}