parsql_migrations/
error.rs

1//! Error types for the migration system.
2
3use thiserror::Error;
4
5/// Main error type for migration operations
6#[derive(Debug, Error)]
7pub enum MigrationError {
8    /// Database operation failed
9    #[error("Database error: {0}")]
10    DatabaseError(String),
11    
12    /// Migration has already been applied
13    #[error("Migration {0} has already been applied")]
14    AlreadyApplied(i64),
15    
16    /// Migration was not found
17    #[error("Migration {0} not found")]
18    NotFound(i64),
19    
20    /// Checksum verification failed
21    #[error("Checksum mismatch for migration {version}: expected {expected}, got {actual}")]
22    ChecksumMismatch {
23        /// Migration version
24        version: i64,
25        /// Expected checksum
26        expected: String,
27        /// Actual checksum
28        actual: String,
29    },
30    
31    /// Lock acquisition failed
32    #[error("Failed to acquire migration lock: {0}")]
33    LockError(String),
34    
35    /// Migration is in a failed state
36    #[error("Migration {0} is in a failed state and must be resolved manually")]
37    FailedState(i64),
38    
39    /// Invalid migration version
40    #[error("Invalid migration version: {0}")]
41    InvalidVersion(i64),
42    
43    /// Migration gap detected
44    #[error("Migration gap detected: missing version {0}")]
45    MigrationGap(i64),
46    
47    /// IO error occurred
48    #[error("IO error: {0}")]
49    IoError(#[from] std::io::Error),
50    
51    /// Serialization error
52    #[error("Serialization error: {0}")]
53    SerializationError(#[from] serde_json::Error),
54    
55    /// Custom error
56    #[error("{0}")]
57    Custom(String),
58}
59
60impl MigrationError {
61    /// Create a new database error
62    pub fn database<S: Into<String>>(msg: S) -> Self {
63        Self::DatabaseError(msg.into())
64    }
65    
66    /// Create a new custom error
67    pub fn custom<S: Into<String>>(msg: S) -> Self {
68        Self::Custom(msg.into())
69    }
70}
71
72/// Result type alias for migration operations
73pub type Result<T> = std::result::Result<T, MigrationError>;
74
75// Database-specific error conversions
76// Note: postgres::Error and tokio_postgres::Error are the same type,
77// so we only need one implementation
78#[cfg(any(feature = "postgres", feature = "tokio-postgres"))]
79impl From<postgres::Error> for MigrationError {
80    fn from(err: postgres::Error) -> Self {
81        Self::DatabaseError(err.to_string())
82    }
83}
84
85#[cfg(feature = "sqlite")]
86impl From<rusqlite::Error> for MigrationError {
87    fn from(err: rusqlite::Error) -> Self {
88        Self::DatabaseError(err.to_string())
89    }
90}
91
92#[cfg(feature = "deadpool-postgres")]
93impl From<deadpool_postgres::PoolError> for MigrationError {
94    fn from(err: deadpool_postgres::PoolError) -> Self {
95        Self::DatabaseError(format!("Connection pool error: {}", err))
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    
103    #[test]
104    fn test_error_display() {
105        let err = MigrationError::AlreadyApplied(1);
106        assert_eq!(err.to_string(), "Migration 1 has already been applied");
107        
108        let err = MigrationError::ChecksumMismatch {
109            version: 2,
110            expected: "abc123".to_string(),
111            actual: "def456".to_string(),
112        };
113        assert_eq!(
114            err.to_string(),
115            "Checksum mismatch for migration 2: expected abc123, got def456"
116        );
117    }
118    
119    #[test]
120    fn test_custom_errors() {
121        let err = MigrationError::database("connection failed");
122        assert_eq!(err.to_string(), "Database error: connection failed");
123        
124        let err = MigrationError::custom("something went wrong");
125        assert_eq!(err.to_string(), "something went wrong");
126    }
127}