1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9pub enum MigrationState {
10 Applied,
12 Failed,
14 InProgress,
16 RolledBack,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct MigrationDetails {
23 pub version: i64,
25 pub name: String,
27 pub state: MigrationState,
29 pub applied_at: Option<DateTime<Utc>>,
31 pub rolled_back_at: Option<DateTime<Utc>>,
33 pub checksum: Option<String>,
35 pub execution_time_ms: Option<i64>,
37 pub error_message: Option<String>,
39}
40
41impl MigrationDetails {
42 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 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 pub fn mark_failed(&mut self, error: String) {
66 self.state = MigrationState::Failed;
67 self.error_message = Some(error);
68 }
69
70 pub fn mark_rolled_back(&mut self) {
72 self.state = MigrationState::RolledBack;
73 self.rolled_back_at = Some(Utc::now());
74 }
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct MigrationStatus {
80 pub version: i64,
82 pub name: String,
84 pub applied: bool,
86 pub applied_at: Option<DateTime<Utc>>,
88 pub execution_time_ms: Option<i64>,
90}
91
92#[derive(Debug, Default, Serialize, Deserialize)]
94pub struct MigrationReport {
95 pub successful: Vec<MigrationResult>,
97 pub failed: Vec<MigrationResult>,
99 pub skipped: Vec<i64>,
101 pub total_time_ms: i64,
103 pub started_at: DateTime<Utc>,
105 pub completed_at: Option<DateTime<Utc>>,
107}
108
109impl MigrationReport {
110 pub fn new() -> Self {
112 Self {
113 started_at: Utc::now(),
114 ..Default::default()
115 }
116 }
117
118 pub fn add_success(&mut self, result: MigrationResult) {
120 self.successful.push(result);
121 }
122
123 pub fn add_failure(&mut self, result: MigrationResult) {
125 self.failed.push(result);
126 }
127
128 pub fn add_skipped(&mut self, version: i64) {
130 self.skipped.push(version);
131 }
132
133 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 pub fn successful_count(&self) -> usize {
143 self.successful.len()
144 }
145
146 pub fn failed_count(&self) -> usize {
148 self.failed.len()
149 }
150
151 pub fn is_success(&self) -> bool {
153 self.failed.is_empty()
154 }
155
156 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#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct MigrationResult {
171 pub version: i64,
173 pub name: String,
175 pub success: bool,
177 pub error: Option<String>,
179 pub execution_time_ms: i64,
181 pub executed_at: DateTime<Utc>,
183}
184
185impl MigrationResult {
186 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 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#[derive(Debug, Clone)]
213pub struct TableConfig {
214 pub table_name: String,
216 pub version_column: String,
218 pub name_column: String,
220 pub applied_at_column: String,
222 pub checksum_column: String,
224 pub execution_time_column: String,
226 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
244pub 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}