parsql_migrations/
types.rs

1//! Common types used throughout the migration system.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7/// Status of a migration
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9pub enum MigrationState {
10    /// Migration has been successfully applied
11    Applied,
12    /// Migration failed during execution
13    Failed,
14    /// Migration is currently being applied
15    InProgress,
16    /// Migration has been rolled back
17    RolledBack,
18}
19
20/// Detailed information about a single migration
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct MigrationDetails {
23    /// Unique version identifier
24    pub version: i64,
25    /// Human-readable name
26    pub name: String,
27    /// Current state of the migration
28    pub state: MigrationState,
29    /// When the migration was applied
30    pub applied_at: Option<DateTime<Utc>>,
31    /// When the migration was rolled back (if applicable)
32    pub rolled_back_at: Option<DateTime<Utc>>,
33    /// Checksum for verification
34    pub checksum: Option<String>,
35    /// Execution time in milliseconds
36    pub execution_time_ms: Option<i64>,
37    /// Error message if migration failed
38    pub error_message: Option<String>,
39}
40
41impl MigrationDetails {
42    /// Create a new migration details instance
43    pub fn new(version: i64, name: String) -> Self {
44        Self {
45            version,
46            name,
47            state: MigrationState::InProgress,
48            applied_at: None,
49            rolled_back_at: None,
50            checksum: None,
51            execution_time_ms: None,
52            error_message: None,
53        }
54    }
55    
56    /// Mark migration as successfully applied
57    pub fn mark_applied(&mut self, execution_time_ms: i64) {
58        self.state = MigrationState::Applied;
59        self.applied_at = Some(Utc::now());
60        self.execution_time_ms = Some(execution_time_ms);
61        self.error_message = None;
62    }
63    
64    /// Mark migration as failed
65    pub fn mark_failed(&mut self, error: String) {
66        self.state = MigrationState::Failed;
67        self.error_message = Some(error);
68    }
69    
70    /// Mark migration as rolled back
71    pub fn mark_rolled_back(&mut self) {
72        self.state = MigrationState::RolledBack;
73        self.rolled_back_at = Some(Utc::now());
74    }
75}
76
77/// Status information for a migration
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct MigrationStatus {
80    /// Migration version
81    pub version: i64,
82    /// Migration name
83    pub name: String,
84    /// Whether the migration has been applied
85    pub applied: bool,
86    /// When the migration was applied
87    pub applied_at: Option<DateTime<Utc>>,
88    /// Execution time in milliseconds
89    pub execution_time_ms: Option<i64>,
90}
91
92/// Report of migration operations
93#[derive(Debug, Default, Serialize, Deserialize)]
94pub struct MigrationReport {
95    /// Successfully applied migrations
96    pub successful: Vec<MigrationResult>,
97    /// Failed migrations
98    pub failed: Vec<MigrationResult>,
99    /// Skipped migrations (already applied)
100    pub skipped: Vec<i64>,
101    /// Total execution time in milliseconds
102    pub total_time_ms: i64,
103    /// Start time of the operation
104    pub started_at: DateTime<Utc>,
105    /// End time of the operation
106    pub completed_at: Option<DateTime<Utc>>,
107}
108
109impl MigrationReport {
110    /// Create a new migration report
111    pub fn new() -> Self {
112        Self {
113            started_at: Utc::now(),
114            ..Default::default()
115        }
116    }
117    
118    /// Add a successful migration result
119    pub fn add_success(&mut self, result: MigrationResult) {
120        self.successful.push(result);
121    }
122    
123    /// Add a failed migration result
124    pub fn add_failure(&mut self, result: MigrationResult) {
125        self.failed.push(result);
126    }
127    
128    /// Add a skipped migration
129    pub fn add_skipped(&mut self, version: i64) {
130        self.skipped.push(version);
131    }
132    
133    /// Mark the report as completed
134    pub fn complete(&mut self) {
135        self.completed_at = Some(Utc::now());
136        if let Some(completed) = self.completed_at {
137            self.total_time_ms = (completed - self.started_at).num_milliseconds();
138        }
139    }
140    
141    /// Get the number of successful migrations
142    pub fn successful_count(&self) -> usize {
143        self.successful.len()
144    }
145    
146    /// Get the number of failed migrations
147    pub fn failed_count(&self) -> usize {
148        self.failed.len()
149    }
150    
151    /// Check if all migrations were successful
152    pub fn is_success(&self) -> bool {
153        self.failed.is_empty()
154    }
155    
156    /// Get a summary of the report
157    pub fn summary(&self) -> String {
158        format!(
159            "Migration Report: {} successful, {} failed, {} skipped ({}ms)",
160            self.successful_count(),
161            self.failed_count(),
162            self.skipped.len(),
163            self.total_time_ms
164        )
165    }
166}
167
168/// Result of a single migration operation
169#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct MigrationResult {
171    /// Migration version
172    pub version: i64,
173    /// Migration name
174    pub name: String,
175    /// Whether the operation was successful
176    pub success: bool,
177    /// Error message if failed
178    pub error: Option<String>,
179    /// Execution time in milliseconds
180    pub execution_time_ms: i64,
181    /// Timestamp of execution
182    pub executed_at: DateTime<Utc>,
183}
184
185impl MigrationResult {
186    /// Create a successful migration result
187    pub fn success(version: i64, name: String, execution_time_ms: i64) -> Self {
188        Self {
189            version,
190            name,
191            success: true,
192            error: None,
193            execution_time_ms,
194            executed_at: Utc::now(),
195        }
196    }
197    
198    /// Create a failed migration result
199    pub fn failure(version: i64, name: String, error: String, execution_time_ms: i64) -> Self {
200        Self {
201            version,
202            name,
203            success: false,
204            error: Some(error),
205            execution_time_ms,
206            executed_at: Utc::now(),
207        }
208    }
209}
210
211/// Configuration for table names and columns
212#[derive(Debug, Clone)]
213pub struct TableConfig {
214    /// Name of the migrations table
215    pub table_name: String,
216    /// Name of the version column
217    pub version_column: String,
218    /// Name of the name column
219    pub name_column: String,
220    /// Name of the applied_at column
221    pub applied_at_column: String,
222    /// Name of the checksum column
223    pub checksum_column: String,
224    /// Name of the execution_time_ms column
225    pub execution_time_column: String,
226    /// Name of the rolled_back_at column
227    pub rolled_back_at_column: String,
228}
229
230impl Default for TableConfig {
231    fn default() -> Self {
232        Self {
233            table_name: "parsql_migrations".to_string(),
234            version_column: "version".to_string(),
235            name_column: "name".to_string(),
236            applied_at_column: "applied_at".to_string(),
237            checksum_column: "checksum".to_string(),
238            execution_time_column: "execution_time_ms".to_string(),
239            rolled_back_at_column: "rolled_back_at".to_string(),
240        }
241    }
242}
243
244/// Type alias for migration version to details mapping
245pub type MigrationMap = HashMap<i64, MigrationDetails>;
246
247#[cfg(test)]
248mod tests {
249    use super::*;
250    
251    #[test]
252    fn test_migration_report() {
253        let mut report = MigrationReport::new();
254        
255        report.add_success(MigrationResult::success(1, "test1".to_string(), 100));
256        report.add_failure(MigrationResult::failure(
257            2, 
258            "test2".to_string(), 
259            "error".to_string(), 
260            50
261        ));
262        report.add_skipped(3);
263        
264        assert_eq!(report.successful_count(), 1);
265        assert_eq!(report.failed_count(), 1);
266        assert!(!report.is_success());
267        
268        report.complete();
269        assert!(report.completed_at.is_some());
270    }
271    
272    #[test]
273    fn test_migration_details() {
274        let mut details = MigrationDetails::new(1, "test".to_string());
275        assert_eq!(details.state, MigrationState::InProgress);
276        
277        details.mark_applied(100);
278        assert_eq!(details.state, MigrationState::Applied);
279        assert!(details.applied_at.is_some());
280        assert_eq!(details.execution_time_ms, Some(100));
281        
282        details.mark_rolled_back();
283        assert_eq!(details.state, MigrationState::RolledBack);
284        assert!(details.rolled_back_at.is_some());
285    }
286}