db_migrate/commands/
verify.rs1
2use crate::{migration::MigrationManager, CommandOutput, MigrationError};
3use anyhow::Result;
4use clap::Args;
5use colored::*;
6
7#[derive(Args)]
8pub struct VerifyCommand {
9 #[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 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 manager.update_migration_checksum(version, new_checksum).await?;
180 Ok(())
181 }
182}