db_migrate/commands/
verify.rs

1
2use crate::{migration::MigrationManager, CommandOutput, MigrationError};
3use anyhow::Result;
4use clap::Args;
5use colored::*;
6
7#[derive(Args)]
8pub struct VerifyCommand {
9    /// Fix checksum mismatches automatically (dangerous)
10    #[arg(long)]
11    fix: bool,
12}
13
14impl VerifyCommand {
15    pub async fn execute(&self, manager: &MigrationManager) -> Result<CommandOutput> {
16        let errors = manager.verify_migrations().await?;
17
18        if errors.is_empty() {
19            return Ok(CommandOutput::success(format!(
20                "{} All migrations verified successfully - no integrity issues found",
21                "✅".green()
22            )));
23        }
24
25        let mut output = Vec::new();
26        output.push(format!("{} Migration integrity issues found:", "⚠️ ".yellow()));
27        output.push(String::new());
28
29        let mut checksum_errors = Vec::new();
30        let mut missing_errors = Vec::new();
31
32        for error in &errors {
33            match error {
34                MigrationError::ChecksumMismatch { version, expected, actual } => {
35                    checksum_errors.push((version, expected, actual));
36                    output.push(format!(
37                        "  {} Checksum mismatch for migration: {}",
38                        "❌".red(),
39                        version.bright_cyan()
40                    ));
41                    output.push(format!(
42                        "     Expected: {}",
43                        expected.dimmed()
44                    ));
45                    output.push(format!(
46                        "     Actual:   {}",
47                        actual.dimmed()
48                    ));
49                    output.push(String::new());
50                }
51                MigrationError::MigrationNotFound(version) => {
52                    missing_errors.push(version);
53                    output.push(format!(
54                        "  {} Migration file missing: {}",
55                        "❌".red(),
56                        version.bright_cyan()
57                    ));
58                    output.push(String::new());
59                }
60                _ => {
61                    output.push(format!(
62                        "  {} Other error: {}",
63                        "❌".red(),
64                        error.to_string()
65                    ));
66                    output.push(String::new());
67                }
68            }
69        }
70
71        // Summary
72        output.push("Summary:".bold().to_string());
73        if !checksum_errors.is_empty() {
74            output.push(format!(
75                "  • {} migration(s) with checksum mismatches",
76                checksum_errors.len()
77            ));
78        }
79        if !missing_errors.is_empty() {
80            output.push(format!(
81                "  • {} migration(s) with missing files",
82                missing_errors.len()
83            ));
84        }
85
86        output.push(String::new());
87
88        if self.fix && !checksum_errors.is_empty() {
89            output.push(format!("{} Attempting to fix checksum mismatches...", "🔧".cyan()));
90
91            let mut fixed_count = 0;
92            for (version, _expected, actual) in &checksum_errors {
93                match self.fix_checksum_mismatch(manager, version, actual).await {
94                    Ok(_) => {
95                        fixed_count += 1;
96                        output.push(format!(
97                            "  {} Fixed checksum for: {}",
98                            "✅".green(),
99                            version.bright_cyan()
100                        ));
101                    }
102                    Err(e) => {
103                        output.push(format!(
104                            "  {} Failed to fix {}: {}",
105                            "❌".red(),
106                            version.bright_cyan(),
107                            e.to_string().dimmed()
108                        ));
109                    }
110                }
111            }
112
113            if fixed_count > 0 {
114                output.push(String::new());
115                output.push(format!(
116                    "{} Fixed {} checksum mismatch(es)",
117                    "✅".green(),
118                    fixed_count
119                ));
120            }
121        } else if !checksum_errors.is_empty() {
122            output.push(format!(
123                "{} Use --fix to automatically update checksums in the database",
124                "💡".bright_blue()
125            ));
126        }
127
128        if !missing_errors.is_empty() {
129            output.push(format!(
130                "{} Missing migration files cannot be automatically fixed",
131                "⚠️ ".yellow()
132            ));
133            output.push("   These migrations were applied but their files are missing.".dimmed().to_string());
134            output.push("   You may need to recreate them or remove the records manually.".dimmed().to_string());
135        }
136
137        Ok(CommandOutput::success_with_data(
138            output.join("\n"),
139            serde_json::json!({
140                "integrity_issues": errors.len(),
141                "checksum_mismatches": checksum_errors.len(),
142                "missing_files": missing_errors.len(),
143                "fixed": self.fix,
144                "issues": errors.iter().map(|e| {
145                    match e {
146                        MigrationError::ChecksumMismatch { version, expected, actual } => {
147                            serde_json::json!({
148                                "type": "checksum_mismatch",
149                                "version": version,
150                                "expected_checksum": expected,
151                                "actual_checksum": actual
152                            })
153                        }
154                        MigrationError::MigrationNotFound(version) => {
155                            serde_json::json!({
156                                "type": "missing_file",
157                                "version": version
158                            })
159                        }
160                        _ => {
161                            serde_json::json!({
162                                "type": "other",
163                                "error": e.to_string()
164                            })
165                        }
166                    }
167                }).collect::<Vec<_>>()
168            })
169        ))
170    }
171
172    async fn fix_checksum_mismatch(
173        &self,
174        manager: &MigrationManager,
175        version: &str,
176        new_checksum: &str,
177    ) -> Result<()> {
178        // We'll need to add this method to MigrationManager
179        manager.update_migration_checksum(version, new_checksum).await?;
180        Ok(())
181    }
182}