version_migrate/
migrator.rs

1//! Migration manager and builder pattern for defining type-safe migration paths.
2
3use 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
12/// A registered migration path for a specific entity type.
13struct EntityMigrationPath {
14    /// Maps version -> migration function to next version
15    steps: HashMap<String, MigrationFn>,
16    /// The final conversion to domain model
17    finalize: Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value, MigrationError>>,
18    /// Ordered list of versions in the migration path
19    versions: Vec<String>,
20    /// The key name for version field in serialized data
21    version_key: String,
22    /// The key name for data field in serialized data
23    data_key: String,
24}
25
26/// The migration manager that orchestrates all migrations.
27pub struct Migrator {
28    paths: HashMap<String, EntityMigrationPath>,
29    default_version_key: Option<String>,
30    default_data_key: Option<String>,
31}
32
33impl Migrator {
34    /// Creates a new, empty migrator.
35    pub fn new() -> Self {
36        Self {
37            paths: HashMap::new(),
38            default_version_key: None,
39            default_data_key: None,
40        }
41    }
42
43    /// Gets the latest version for a given entity.
44    ///
45    /// # Returns
46    ///
47    /// The latest version string if the entity is registered, `None` otherwise.
48    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    /// Creates a builder for configuring the migrator.
56    ///
57    /// # Example
58    ///
59    /// ```ignore
60    /// let migrator = Migrator::builder()
61    ///     .default_version_key("schema_version")
62    ///     .default_data_key("payload")
63    ///     .build();
64    /// ```
65    pub fn builder() -> MigratorBuilder {
66        MigratorBuilder::new()
67    }
68
69    /// Starts defining a migration path for an entity.
70    pub fn define(entity: &str) -> MigrationPathBuilder<Start> {
71        MigrationPathBuilder::new(entity.to_string())
72    }
73
74    /// Registers a migration path with validation.
75    ///
76    /// This method validates the migration path before registering it:
77    /// - Checks for circular migration paths
78    /// - Validates version ordering follows semver rules
79    ///
80    /// # Errors
81    ///
82    /// Returns an error if validation fails.
83    pub fn register<D>(&mut self, path: MigrationPath<D>) -> Result<(), MigrationError> {
84        Self::validate_migration_path(&path.entity, &path.versions)?;
85
86        // Resolve key priority: Path custom > Migrator default > EntityPath (trait constants)
87        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    /// Validates a migration path for correctness.
110    fn validate_migration_path(entity: &str, versions: &[String]) -> Result<(), MigrationError> {
111        // Check for circular paths
112        Self::check_circular_path(entity, versions)?;
113
114        // Check version ordering
115        Self::check_version_ordering(entity, versions)?;
116
117        Ok(())
118    }
119
120    /// Checks if there are any circular dependencies in the migration path.
121    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                // Found a duplicate - circular path detected
127                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    /// Checks if versions are ordered according to semver rules.
139    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            // Parse versions
145            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            // Check that next version is greater than current
154            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    /// Loads and migrates data from any serde-compatible format.
167    ///
168    /// This is the generic version that accepts any type implementing `Serialize`.
169    /// For JSON strings, use the convenience method `load` instead.
170    ///
171    /// # Arguments
172    ///
173    /// * `entity` - The entity name used when registering the migration path
174    /// * `data` - Versioned data in any serde-compatible format (e.g., `toml::Value`, `serde_json::Value`)
175    ///
176    /// # Returns
177    ///
178    /// The migrated data as the domain model type
179    ///
180    /// # Errors
181    ///
182    /// Returns an error if:
183    /// - The data cannot be converted to the internal format
184    /// - The entity is not registered
185    /// - A migration step fails
186    ///
187    /// # Example
188    ///
189    /// ```ignore
190    /// // Load from TOML
191    /// let toml_data: toml::Value = toml::from_str(toml_str)?;
192    /// let domain: TaskEntity = migrator.load_from("task", toml_data)?;
193    ///
194    /// // Load from JSON Value
195    /// let json_data: serde_json::Value = serde_json::from_str(json_str)?;
196    /// let domain: TaskEntity = migrator.load_from("task", json_data)?;
197    /// ```
198    pub fn load_from<D, T>(&self, entity: &str, data: T) -> Result<D, MigrationError>
199    where
200        D: DeserializeOwned,
201        T: Serialize,
202    {
203        // Convert the input data to serde_json::Value for internal processing
204        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        // Get the migration path for this entity
212        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        // Extract version and data using custom keys
221        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        // Apply migration steps until we reach a version with no further steps
248        while let Some(migrate_fn) = path.steps.get(&current_version) {
249            // Migration function returns raw value, no wrapping
250            current_data = migrate_fn(current_data.clone())?;
251
252            // Update version to the next step
253            // Find the next version in the path
254            match path.versions.iter().position(|v| v == &current_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        // Finalize into domain model
263        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    /// Loads and migrates data from a JSON string.
271    ///
272    /// This is a convenience method for the common case of loading from JSON.
273    /// For other formats, use `load_from` instead.
274    ///
275    /// # Arguments
276    ///
277    /// * `entity` - The entity name used when registering the migration path
278    /// * `json` - A JSON string containing versioned data
279    ///
280    /// # Returns
281    ///
282    /// The migrated data as the domain model type
283    ///
284    /// # Errors
285    ///
286    /// Returns an error if:
287    /// - The JSON cannot be parsed
288    /// - The entity is not registered
289    /// - A migration step fails
290    ///
291    /// # Example
292    ///
293    /// ```ignore
294    /// let json = r#"{"version":"1.0.0","data":{"id":"task-1","title":"My Task"}}"#;
295    /// let domain: TaskEntity = migrator.load("task", json)?;
296    /// ```
297    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    /// Loads and migrates data from a flat format JSON string.
305    ///
306    /// This is a convenience method for loading from flat format JSON where the version
307    /// field is at the same level as the data fields.
308    ///
309    /// # Arguments
310    ///
311    /// * `entity` - The entity name used when registering the migration path
312    /// * `json` - A JSON string containing versioned data in flat format
313    ///
314    /// # Returns
315    ///
316    /// The migrated data as the domain model type
317    ///
318    /// # Errors
319    ///
320    /// Returns an error if:
321    /// - The JSON cannot be parsed
322    /// - The entity is not registered
323    /// - A migration step fails
324    ///
325    /// # Example
326    ///
327    /// ```ignore
328    /// let json = r#"{"version":"1.0.0","id":"task-1","title":"My Task"}"#;
329    /// let domain: TaskEntity = migrator.load_flat("task", json)?;
330    /// ```
331    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    /// Loads and migrates data from any serde-compatible format in flat format.
343    ///
344    /// This method expects the version field to be at the same level as the data fields.
345    /// It uses the registered migration path's runtime-configured keys (respecting the
346    /// Path > Migrator > Trait priority).
347    ///
348    /// # Arguments
349    ///
350    /// * `entity` - The entity name used when registering the migration path
351    /// * `value` - A serde-compatible value containing versioned data in flat format
352    ///
353    /// # Returns
354    ///
355    /// The migrated data as the domain model type
356    ///
357    /// # Errors
358    ///
359    /// Returns an error if:
360    /// - The entity is not registered
361    /// - The data format is invalid
362    /// - A migration step fails
363    ///
364    /// # Example
365    ///
366    /// ```ignore
367    /// let toml_value: toml::Value = toml::from_str(toml_str)?;
368    /// let domain: TaskEntity = migrator.load_flat_from("task", toml_value)?;
369    /// ```
370    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        // Convert to serde_json::Value for manipulation
383        let mut value = serde_json::to_value(value).map_err(|e| {
384            MigrationError::SerializationError(format!("Failed to convert input: {}", e))
385        })?;
386
387        // Extract version from the flat structure
388        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        // Now obj contains only data fields (version has been removed)
412        let mut current_data = serde_json::Value::Object(obj.clone());
413        let mut current_version = current_version;
414
415        // Apply migration steps until we reach a version with no further steps
416        while let Some(migrate_fn) = path.steps.get(&current_version) {
417            // Migration function returns raw value, no wrapping
418            current_data = migrate_fn(current_data.clone())?;
419
420            // Update version to the next step
421            match path.versions.iter().position(|v| v == &current_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        // Finalize into domain model
430        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    /// Saves versioned data to a JSON string.
438    ///
439    /// This method wraps the provided data with its version information and serializes
440    /// it to JSON format. The resulting JSON can later be loaded and migrated using
441    /// the `load` method.
442    ///
443    /// # Arguments
444    ///
445    /// * `data` - The versioned data to save
446    ///
447    /// # Returns
448    ///
449    /// A JSON string with the format: `{"version":"x.y.z","data":{...}}`
450    ///
451    /// # Errors
452    ///
453    /// Returns `SerializationError` if the data cannot be serialized to JSON.
454    ///
455    /// # Example
456    ///
457    /// ```ignore
458    /// let task = TaskV1_0_0 {
459    ///     id: "task-1".to_string(),
460    ///     title: "My Task".to_string(),
461    /// };
462    ///
463    /// let migrator = Migrator::new();
464    /// let json = migrator.save(task)?;
465    /// // json: {"version":"1.0.0","data":{"id":"task-1","title":"My Task"}}
466    /// ```
467    pub fn save<T: Versioned + Serialize>(&self, data: T) -> Result<String, MigrationError> {
468        // Use custom keys from the type's Versioned trait
469        let version_key = T::VERSION_KEY;
470        let data_key = T::DATA_KEY;
471
472        // Serialize the data
473        let data_value = serde_json::to_value(&data).map_err(|e| {
474            MigrationError::SerializationError(format!("Failed to serialize data: {}", e))
475        })?;
476
477        // Build the wrapper with custom keys
478        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    /// Saves versioned data to a JSON string in flat format.
491    ///
492    /// Unlike `save()`, this method produces a flat JSON structure where the version
493    /// field is at the same level as the data fields, not wrapped in a separate object.
494    ///
495    /// # Arguments
496    ///
497    /// * `data` - The versioned data to save
498    ///
499    /// # Returns
500    ///
501    /// A JSON string with the format: `{"version":"x.y.z","field1":"value1",...}`
502    ///
503    /// # Errors
504    ///
505    /// Returns `SerializationError` if the data cannot be serialized to JSON.
506    ///
507    /// # Example
508    ///
509    /// ```ignore
510    /// let task = TaskV1_0_0 {
511    ///     id: "task-1".to_string(),
512    ///     title: "My Task".to_string(),
513    /// };
514    ///
515    /// let migrator = Migrator::new();
516    /// let json = migrator.save_flat(task)?;
517    /// // json: {"version":"1.0.0","id":"task-1","title":"My Task"}
518    /// ```
519    pub fn save_flat<T: Versioned + Serialize>(&self, data: T) -> Result<String, MigrationError> {
520        let version_key = T::VERSION_KEY;
521
522        // Serialize the data to a JSON object
523        let mut data_value = serde_json::to_value(&data).map_err(|e| {
524            MigrationError::SerializationError(format!("Failed to serialize data: {}", e))
525        })?;
526
527        // Ensure it's an object so we can add the version field
528        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        // Add the version field to the same level as data fields
535        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    /// Loads and migrates multiple entities from any serde-compatible format.
546    ///
547    /// This is the generic version that accepts any type implementing `Serialize`.
548    /// For JSON arrays, use the convenience method `load_vec` instead.
549    ///
550    /// # Arguments
551    ///
552    /// * `entity` - The entity name used when registering the migration path
553    /// * `data` - Array of versioned data in any serde-compatible format
554    ///
555    /// # Returns
556    ///
557    /// A vector of migrated data as domain model types
558    ///
559    /// # Errors
560    ///
561    /// Returns an error if:
562    /// - The data cannot be converted to the internal format
563    /// - The entity is not registered
564    /// - Any migration step fails
565    ///
566    /// # Example
567    ///
568    /// ```ignore
569    /// // Load from TOML array
570    /// let toml_array: Vec<toml::Value> = /* ... */;
571    /// let domains: Vec<TaskEntity> = migrator.load_vec_from("task", toml_array)?;
572    ///
573    /// // Load from JSON Value array
574    /// let json_array: Vec<serde_json::Value> = /* ... */;
575    /// let domains: Vec<TaskEntity> = migrator.load_vec_from("task", json_array)?;
576    /// ```
577    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    /// Loads and migrates multiple entities from a JSON array string.
588    ///
589    /// This is a convenience method for the common case of loading from a JSON array.
590    /// For other formats, use `load_vec_from` instead.
591    ///
592    /// # Arguments
593    ///
594    /// * `entity` - The entity name used when registering the migration path
595    /// * `json` - A JSON array string containing versioned data
596    ///
597    /// # Returns
598    ///
599    /// A vector of migrated data as domain model types
600    ///
601    /// # Errors
602    ///
603    /// Returns an error if:
604    /// - The JSON cannot be parsed
605    /// - The entity is not registered
606    /// - Any migration step fails
607    ///
608    /// # Example
609    ///
610    /// ```ignore
611    /// let json = r#"[
612    ///     {"version":"1.0.0","data":{"id":"task-1","title":"Task 1"}},
613    ///     {"version":"1.0.0","data":{"id":"task-2","title":"Task 2"}}
614    /// ]"#;
615    /// let domains: Vec<TaskEntity> = migrator.load_vec("task", json)?;
616    /// ```
617    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    /// Loads and migrates multiple entities from a flat format JSON array string.
629    ///
630    /// This is a convenience method for loading from a JSON array where each element
631    /// has the version field at the same level as the data fields.
632    ///
633    /// # Arguments
634    ///
635    /// * `entity` - The entity name used when registering the migration path
636    /// * `json` - A JSON array string containing versioned data in flat format
637    ///
638    /// # Returns
639    ///
640    /// A vector of migrated data as domain model types
641    ///
642    /// # Errors
643    ///
644    /// Returns an error if:
645    /// - The JSON cannot be parsed
646    /// - The entity is not registered
647    /// - Any migration step fails
648    ///
649    /// # Example
650    ///
651    /// ```ignore
652    /// let json = r#"[
653    ///     {"version":"1.0.0","id":"task-1","title":"Task 1"},
654    ///     {"version":"1.0.0","id":"task-2","title":"Task 2"}
655    /// ]"#;
656    /// let domains: Vec<TaskEntity> = migrator.load_vec_flat("task", json)?;
657    /// ```
658    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    /// Loads and migrates multiple entities from any serde-compatible format in flat format.
670    ///
671    /// This method expects each element to have the version field at the same level
672    /// as the data fields. It uses the registered migration path's runtime-configured
673    /// keys (respecting the Path > Migrator > Trait priority).
674    ///
675    /// # Arguments
676    ///
677    /// * `entity` - The entity name used when registering the migration path
678    /// * `data` - Vector of serde-compatible values in flat format
679    ///
680    /// # Returns
681    ///
682    /// A vector of migrated data as domain model types
683    ///
684    /// # Errors
685    ///
686    /// Returns an error if:
687    /// - The entity is not registered
688    /// - The data format is invalid
689    /// - Any migration step fails
690    ///
691    /// # Example
692    ///
693    /// ```ignore
694    /// let toml_array: Vec<toml::Value> = /* ... */;
695    /// let domains: Vec<TaskEntity> = migrator.load_vec_flat_from("task", toml_array)?;
696    /// ```
697    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    /// Saves multiple versioned entities to a JSON array string.
712    ///
713    /// This method wraps each item with its version information and serializes
714    /// them as a JSON array. The resulting JSON can later be loaded and migrated
715    /// using the `load_vec` method.
716    ///
717    /// # Arguments
718    ///
719    /// * `data` - Vector of versioned data to save
720    ///
721    /// # Returns
722    ///
723    /// A JSON array string where each element has the format: `{"version":"x.y.z","data":{...}}`
724    ///
725    /// # Errors
726    ///
727    /// Returns `SerializationError` if the data cannot be serialized to JSON.
728    ///
729    /// # Example
730    ///
731    /// ```ignore
732    /// let tasks = vec![
733    ///     TaskV1_0_0 {
734    ///         id: "task-1".to_string(),
735    ///         title: "Task 1".to_string(),
736    ///     },
737    ///     TaskV1_0_0 {
738    ///         id: "task-2".to_string(),
739    ///         title: "Task 2".to_string(),
740    ///     },
741    /// ];
742    ///
743    /// let migrator = Migrator::new();
744    /// let json = migrator.save_vec(tasks)?;
745    /// // json: [{"version":"1.0.0","data":{"id":"task-1",...}}, ...]
746    /// ```
747    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    /// Saves multiple versioned entities to a JSON array string in flat format.
778    ///
779    /// This method serializes each item with the version field at the same level
780    /// as the data fields, not wrapped in a separate object.
781    ///
782    /// # Arguments
783    ///
784    /// * `data` - Vector of versioned data to save
785    ///
786    /// # Returns
787    ///
788    /// A JSON array string where each element has the format: `{"version":"x.y.z","field1":"value1",...}`
789    ///
790    /// # Errors
791    ///
792    /// Returns `SerializationError` if the data cannot be serialized to JSON.
793    ///
794    /// # Example
795    ///
796    /// ```ignore
797    /// let tasks = vec![
798    ///     TaskV1_0_0 {
799    ///         id: "task-1".to_string(),
800    ///         title: "Task 1".to_string(),
801    ///     },
802    ///     TaskV1_0_0 {
803    ///         id: "task-2".to_string(),
804    ///         title: "Task 2".to_string(),
805    ///     },
806    /// ];
807    ///
808    /// let migrator = Migrator::new();
809    /// let json = migrator.save_vec_flat(tasks)?;
810    /// // json: [{"version":"1.0.0","id":"task-1",...}, ...]
811    /// ```
812    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                // Add version field at the same level
832                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    /// Saves a domain entity to a JSON string using its latest versioned format.
850    ///
851    /// This method automatically converts the domain entity to its latest version
852    /// and saves it with version information.
853    ///
854    /// # Arguments
855    ///
856    /// * `entity` - The domain entity to save (must implement `LatestVersioned`)
857    ///
858    /// # Returns
859    ///
860    /// A JSON string with the format: `{"version":"x.y.z","data":{...}}`
861    ///
862    /// # Errors
863    ///
864    /// Returns `SerializationError` if the entity cannot be serialized to JSON.
865    ///
866    /// # Example
867    ///
868    /// ```ignore
869    /// #[version_migrate(entity = "task", latest = TaskV1_1_0)]
870    /// struct TaskEntity {
871    ///     id: String,
872    ///     title: String,
873    ///     description: Option<String>,
874    /// }
875    ///
876    /// let entity = TaskEntity {
877    ///     id: "task-1".to_string(),
878    ///     title: "My Task".to_string(),
879    ///     description: Some("Description".to_string()),
880    /// };
881    ///
882    /// let migrator = Migrator::new();
883    /// let json = migrator.save_entity(entity)?;
884    /// // Automatically saved with latest version (1.1.0)
885    /// ```
886    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    /// Saves a domain entity to a JSON string in flat format using its latest versioned format.
895    ///
896    /// This method automatically converts the domain entity to its latest version
897    /// and saves it with the version field at the same level as data fields.
898    ///
899    /// # Arguments
900    ///
901    /// * `entity` - The domain entity to save (must implement `LatestVersioned`)
902    ///
903    /// # Returns
904    ///
905    /// A JSON string with the format: `{"version":"x.y.z","field1":"value1",...}`
906    ///
907    /// # Errors
908    ///
909    /// Returns `SerializationError` if the entity cannot be serialized to JSON.
910    ///
911    /// # Example
912    ///
913    /// ```ignore
914    /// #[version_migrate(entity = "task", latest = TaskV1_1_0)]
915    /// struct TaskEntity {
916    ///     id: String,
917    ///     title: String,
918    ///     description: Option<String>,
919    /// }
920    ///
921    /// let entity = TaskEntity {
922    ///     id: "task-1".to_string(),
923    ///     title: "My Task".to_string(),
924    ///     description: Some("Description".to_string()),
925    /// };
926    ///
927    /// let migrator = Migrator::new();
928    /// let json = migrator.save_entity_flat(entity)?;
929    /// // json: {"version":"1.1.0","id":"task-1","title":"My Task",...}
930    /// ```
931    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    /// Saves multiple domain entities to a JSON array string using their latest versioned format.
940    ///
941    /// This method automatically converts each domain entity to its latest version
942    /// and saves them as a JSON array.
943    ///
944    /// # Arguments
945    ///
946    /// * `entities` - Vector of domain entities to save
947    ///
948    /// # Returns
949    ///
950    /// A JSON array string where each element has the format: `{"version":"x.y.z","data":{...}}`
951    ///
952    /// # Errors
953    ///
954    /// Returns `SerializationError` if the entities cannot be serialized to JSON.
955    ///
956    /// # Example
957    ///
958    /// ```ignore
959    /// let entities = vec![
960    ///     TaskEntity { id: "1".into(), title: "Task 1".into(), description: None },
961    ///     TaskEntity { id: "2".into(), title: "Task 2".into(), description: None },
962    /// ];
963    ///
964    /// let json = migrator.save_entity_vec(entities)?;
965    /// ```
966    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    /// Saves multiple domain entities to a JSON array string in flat format using their latest versioned format.
975    ///
976    /// This method automatically converts each domain entity to its latest version
977    /// and saves them with version fields at the same level as data fields.
978    ///
979    /// # Arguments
980    ///
981    /// * `entities` - Vector of domain entities to save
982    ///
983    /// # Returns
984    ///
985    /// A JSON array string where each element has the format: `{"version":"x.y.z","field1":"value1",...}`
986    ///
987    /// # Errors
988    ///
989    /// Returns `SerializationError` if the entities cannot be serialized to JSON.
990    ///
991    /// # Example
992    ///
993    /// ```ignore
994    /// let entities = vec![
995    ///     TaskEntity { id: "1".into(), title: "Task 1".into(), description: None },
996    ///     TaskEntity { id: "2".into(), title: "Task 2".into(), description: None },
997    /// ];
998    ///
999    /// let json = migrator.save_entity_vec_flat(entities)?;
1000    /// ```
1001    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
1016/// Builder for configuring a `Migrator` with default settings.
1017pub 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    /// Sets the default version key for all entities.
1031    ///
1032    /// This key will be used unless overridden by:
1033    /// - The entity's `MigrationPath` via `with_keys()`
1034    /// - The type's `Versioned` trait constants
1035    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    /// Sets the default data key for all entities.
1041    ///
1042    /// This key will be used unless overridden by:
1043    /// - The entity's `MigrationPath` via `with_keys()`
1044    /// - The type's `Versioned` trait constants
1045    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    /// Builds the `Migrator` with the configured defaults.
1051    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
1060/// Marker type for builder state: start
1061pub struct Start;
1062
1063/// Marker type for builder state: has a starting version
1064pub struct HasFrom<V>(PhantomData<V>);
1065
1066/// Marker type for builder state: has intermediate steps
1067pub struct HasSteps<V>(PhantomData<V>);
1068
1069/// Builder for defining migration paths.
1070pub 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    /// Overrides the version and data keys for this migration path.
1096    ///
1097    /// This takes precedence over both the Migrator's defaults and the type's trait constants.
1098    ///
1099    /// # Example
1100    ///
1101    /// ```ignore
1102    /// Migrator::define("task")
1103    ///     .with_keys("custom_version", "custom_data")
1104    ///     .from::<TaskV1>()
1105    ///     .into::<TaskDomain>();
1106    /// ```
1107    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    /// Sets the starting version for migrations.
1118    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    /// Adds a migration step to the next version.
1140    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            // Return the raw migrated value without wrapping
1158            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    /// Finalizes the migration path with conversion to domain model.
1181    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    /// Adds another migration step.
1225    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            // Return the raw migrated value without wrapping
1243            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    /// Finalizes the migration path with conversion to domain model.
1266    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
1305/// A complete migration path from versioned DTOs to a domain model.
1306pub struct MigrationPath<D> {
1307    entity: String,
1308    inner: EntityMigrationPath,
1309    /// List of versions in the migration path for validation
1310    versions: Vec<String>,
1311    /// Custom version key override (takes precedence over Migrator defaults)
1312    custom_version_key: Option<String>,
1313    /// Custom data key override (takes precedence over Migrator defaults)
1314    custom_data_key: Option<String>,
1315    _phantom: PhantomData<D>,
1316}
1317
1318/// A wrapper around JSON data that provides convenient query and update methods
1319/// for partial updates with automatic migration.
1320///
1321/// `ConfigMigrator` holds a JSON object and allows you to query specific keys,
1322/// automatically migrating versioned data to domain entities, and update them
1323/// with the latest version.
1324///
1325/// # Example
1326///
1327/// ```ignore
1328/// // config.json:
1329/// // {
1330/// //   "app_name": "MyApp",
1331/// //   "tasks": [
1332/// //     {"version": "1.0.0", "id": "1", "title": "Task 1"},
1333/// //     {"version": "2.0.0", "id": "2", "title": "Task 2", "description": "New"}
1334/// //   ]
1335/// // }
1336///
1337/// let config_json = fs::read_to_string("config.json")?;
1338/// let mut config = ConfigMigrator::from(&config_json, migrator)?;
1339///
1340/// // Query tasks (automatically migrates all versions to TaskEntity)
1341/// let mut tasks: Vec<TaskEntity> = config.query("tasks")?;
1342///
1343/// // Update tasks
1344/// tasks[0].title = "Updated Task".to_string();
1345///
1346/// // Save back with latest version
1347/// config.update("tasks", tasks)?;
1348///
1349/// // Write to file
1350/// fs::write("config.json", config.to_string()?)?;
1351/// ```
1352pub struct ConfigMigrator {
1353    root: serde_json::Value,
1354    migrator: Migrator,
1355}
1356
1357impl ConfigMigrator {
1358    /// Creates a new `ConfigMigrator` from a JSON string and a `Migrator`.
1359    ///
1360    /// # Errors
1361    ///
1362    /// Returns `MigrationError::DeserializationError` if the JSON is invalid.
1363    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    /// Queries a specific key from the JSON object and returns the data as domain entities.
1370    ///
1371    /// This method automatically migrates all versioned data to the latest version
1372    /// and converts them to domain entities.
1373    ///
1374    /// # Type Parameters
1375    ///
1376    /// - `T`: Must implement `Queryable` to provide the entity name, and `Deserialize` for deserialization.
1377    ///
1378    /// # Errors
1379    ///
1380    /// - Returns `MigrationError::DeserializationError` if the key doesn't contain a valid array.
1381    /// - Returns migration errors if the data cannot be migrated.
1382    ///
1383    /// # Example
1384    ///
1385    /// ```ignore
1386    /// let tasks: Vec<TaskEntity> = config.query("tasks")?;
1387    /// ```
1388    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(); // Safe because we checked is_array()
1405        self.migrator
1406            .load_vec_flat_from(T::ENTITY_NAME, array.to_vec())
1407    }
1408
1409    /// Updates a specific key in the JSON object with new domain entities.
1410    ///
1411    /// This method serializes the entities with the latest version (automatically
1412    /// determined from the `Queryable` trait) and updates the JSON object in place.
1413    ///
1414    /// # Type Parameters
1415    ///
1416    /// - `T`: Must implement `Serialize` and `Queryable`.
1417    ///
1418    /// # Errors
1419    ///
1420    /// - Returns `MigrationError::EntityNotFound` if the entity is not registered.
1421    /// - Returns serialization errors if the data cannot be serialized.
1422    ///
1423    /// # Example
1424    ///
1425    /// ```ignore
1426    /// // Version is automatically determined from the entity's migration path
1427    /// config.update("tasks", updated_tasks)?;
1428    /// ```
1429    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        // Serialize each item with version field
1440        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    /// Converts the entire JSON object back to a pretty-printed string.
1462    ///
1463    /// # Errors
1464    ///
1465    /// Returns `MigrationError::SerializationError` if serialization fails.
1466    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    /// Converts the entire JSON object to a compact string.
1472    ///
1473    /// # Errors
1474    ///
1475    /// Returns `MigrationError::SerializationError` if serialization fails.
1476    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    /// Returns a reference to the underlying JSON value.
1482    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    // Test data structures
1494    #[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        // Test entity1
1701        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        // Test entity2
1710        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        // Verify JSON contains version and data
1730        assert!(json.contains("\"version\""));
1731        assert!(json.contains("\"1.0.0\""));
1732        assert!(json.contains("\"data\""));
1733        assert!(json.contains("\"test_save\""));
1734
1735        // Verify it can be parsed back
1736        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        // Save V1 data
1752        let v1 = V1 {
1753            value: "roundtrip".to_string(),
1754        };
1755        let json = migrator.save(v1).unwrap();
1756
1757        // Load and migrate to Domain
1758        let domain: Domain = migrator.load("test", &json).unwrap();
1759
1760        assert_eq!(domain.value, "roundtrip");
1761        assert_eq!(domain.count, 0); // Default from V1->V2 migration
1762        assert!(domain.enabled); // Default from V2->V3 migration
1763    }
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        // Verify the JSON structure
1778        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        // Should be compact JSON (not pretty-printed)
1796        assert!(!json.contains('\n'));
1797        assert!(json.contains("\"version\":\"2.0.0\""));
1798    }
1799
1800    #[test]
1801    fn test_validation_invalid_version_order() {
1802        // Manually construct a path with invalid version ordering
1803        let entity = "test".to_string();
1804        let versions = vec!["2.0.0".to_string(), "1.0.0".to_string()]; // Wrong order
1805
1806        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        // Manually construct a path with circular reference
1827        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(), // Circular!
1832        ];
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        // Valid migration path
1850        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        // Empty path should be valid
1864        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        // Single version path should be valid (no steps, just final conversion)
1874        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    // Tests for Vec operations
1882    #[test]
1883    fn test_save_vec_and_load_vec() {
1884        let migrator = Migrator::new();
1885
1886        // Save multiple V1 items
1887        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        // Verify JSON array format
1902        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        // Setup migration path
1910        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        // Load and migrate the array
1920        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        // All should have default values from migration
1928        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        // Setup migration path
1954        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        // JSON with mixed versions
1964        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        // V1 item migrated to domain
1975        assert_eq!(domains[0].value, "v1-item");
1976        assert_eq!(domains[0].count, 0);
1977        assert!(domains[0].enabled);
1978
1979        // V2 item migrated to domain
1980        assert_eq!(domains[1].value, "v2-item");
1981        assert_eq!(domains[1].count, 42);
1982        assert!(domains[1].enabled);
1983
1984        // V3 item converted to domain
1985        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        // Create Vec<serde_json::Value> directly
2002        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        // Verify structure
2074        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}