version_migrate/
errors.rs

1//! Error types for migration operations.
2
3use thiserror::Error;
4
5/// Error types that can occur during migration operations.
6#[derive(Error, Debug)]
7pub enum MigrationError {
8    /// Failed to deserialize the data.
9    #[error("Failed to deserialize: {0}")]
10    DeserializationError(String),
11
12    /// Failed to serialize the data.
13    #[error("Failed to serialize: {0}")]
14    SerializationError(String),
15
16    /// The requested entity type was not found in the migrator.
17    #[error("Entity '{0}' not found")]
18    EntityNotFound(String),
19
20    /// No migration path is defined for the given entity and version.
21    #[error("No migration path defined for entity '{entity}' version '{version}'")]
22    MigrationPathNotDefined {
23        /// The entity name.
24        entity: String,
25        /// The version that has no migration path.
26        version: String,
27    },
28
29    /// A migration step failed during execution.
30    #[error("Migration failed from '{from}' to '{to}': {error}")]
31    MigrationStepFailed {
32        /// The source version.
33        from: String,
34        /// The target version.
35        to: String,
36        /// The error message.
37        error: String,
38    },
39
40    /// A circular migration path was detected.
41    #[error("Circular migration path detected in entity '{entity}': {path}")]
42    CircularMigrationPath {
43        /// The entity name.
44        entity: String,
45        /// The path that forms a cycle.
46        path: String,
47    },
48
49    /// Version ordering is invalid (not following semver rules).
50    #[error("Invalid version order in entity '{entity}': '{from}' -> '{to}' (versions must increase according to semver)")]
51    InvalidVersionOrder {
52        /// The entity name.
53        entity: String,
54        /// The source version.
55        from: String,
56        /// The target version.
57        to: String,
58    },
59
60    /// File I/O error.
61    #[error("File I/O error at '{path}': {error}")]
62    IoError {
63        /// The file path where the error occurred.
64        path: String,
65        /// The underlying I/O error.
66        error: String,
67    },
68
69    /// File locking error.
70    #[error("Failed to acquire file lock for '{path}': {error}")]
71    LockError {
72        /// The file path.
73        path: String,
74        /// The error message.
75        error: String,
76    },
77
78    /// TOML parsing error.
79    #[error("Failed to parse TOML: {0}")]
80    TomlParseError(String),
81
82    /// TOML serialization error.
83    #[error("Failed to serialize to TOML: {0}")]
84    TomlSerializeError(String),
85
86    /// Failed to find home directory.
87    #[error("Cannot determine home directory")]
88    HomeDirNotFound,
89
90    /// Failed to resolve path.
91    #[error("Failed to resolve path: {0}")]
92    PathResolution(String),
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn test_error_display_deserialization() {
101        let err = MigrationError::DeserializationError("invalid JSON".to_string());
102        let display = format!("{}", err);
103        assert!(display.contains("Failed to deserialize"));
104        assert!(display.contains("invalid JSON"));
105    }
106
107    #[test]
108    fn test_error_display_serialization() {
109        let err = MigrationError::SerializationError("invalid data".to_string());
110        let display = format!("{}", err);
111        assert!(display.contains("Failed to serialize"));
112        assert!(display.contains("invalid data"));
113    }
114
115    #[test]
116    fn test_error_display_entity_not_found() {
117        let err = MigrationError::EntityNotFound("user".to_string());
118        let display = format!("{}", err);
119        assert!(display.contains("Entity 'user' not found"));
120    }
121
122    #[test]
123    fn test_error_display_migration_path_not_defined() {
124        let err = MigrationError::MigrationPathNotDefined {
125            entity: "task".to_string(),
126            version: "2.0.0".to_string(),
127        };
128        let display = format!("{}", err);
129        assert!(display.contains("No migration path defined"));
130        assert!(display.contains("task"));
131        assert!(display.contains("2.0.0"));
132    }
133
134    #[test]
135    fn test_error_display_migration_step_failed() {
136        let err = MigrationError::MigrationStepFailed {
137            from: "1.0.0".to_string(),
138            to: "2.0.0".to_string(),
139            error: "field missing".to_string(),
140        };
141        let display = format!("{}", err);
142        assert!(display.contains("Migration failed"));
143        assert!(display.contains("1.0.0"));
144        assert!(display.contains("2.0.0"));
145        assert!(display.contains("field missing"));
146    }
147
148    #[test]
149    fn test_error_debug() {
150        let err = MigrationError::EntityNotFound("test".to_string());
151        let debug = format!("{:?}", err);
152        assert!(debug.contains("EntityNotFound"));
153    }
154
155    #[test]
156    fn test_error_is_std_error() {
157        let err = MigrationError::DeserializationError("test".to_string());
158        // MigrationError should implement std::error::Error
159        let _: &dyn std::error::Error = &err;
160    }
161
162    #[test]
163    fn test_error_display_circular_migration_path() {
164        let err = MigrationError::CircularMigrationPath {
165            entity: "task".to_string(),
166            path: "1.0.0 -> 2.0.0 -> 1.0.0".to_string(),
167        };
168        let display = format!("{}", err);
169        assert!(display.contains("Circular migration path"));
170        assert!(display.contains("task"));
171        assert!(display.contains("1.0.0 -> 2.0.0 -> 1.0.0"));
172    }
173
174    #[test]
175    fn test_error_display_invalid_version_order() {
176        let err = MigrationError::InvalidVersionOrder {
177            entity: "task".to_string(),
178            from: "2.0.0".to_string(),
179            to: "1.0.0".to_string(),
180        };
181        let display = format!("{}", err);
182        assert!(display.contains("Invalid version order"));
183        assert!(display.contains("task"));
184        assert!(display.contains("2.0.0"));
185        assert!(display.contains("1.0.0"));
186        assert!(display.contains("must increase"));
187    }
188}