1use thiserror::Error;
4
5pub fn format_db_error(e: &tokio_postgres::Error) -> String {
8 if let Some(db_err) = e.as_db_error() {
10 let mut msg = db_err.message().to_string();
11 if let Some(detail) = db_err.detail() {
12 msg.push_str(&format!("\n Detail: {}", detail));
13 }
14 if let Some(hint) = db_err.hint() {
15 msg.push_str(&format!("\n Hint: {}", hint));
16 }
17 if let Some(position) = db_err.position() {
18 msg.push_str(&format!("\n Position: {:?}", position));
19 }
20 return msg;
21 }
22 let mut msg = e.to_string();
24 let mut source = std::error::Error::source(e);
25 while let Some(s) = source {
26 msg.push_str(&format!(": {}", s));
27 source = s.source();
28 }
29 if e.is_closed() {
31 msg.push_str("\n Note: The database connection was closed unexpectedly. This may indicate a network issue or server restart.");
32 }
33 msg
34}
35
36#[derive(Error, Debug)]
38pub enum WaypointError {
39 #[error("Configuration error: {0}")]
41 ConfigError(String),
42
43 #[error("Database error: {}", format_db_error(.0))]
45 DatabaseError(#[from] tokio_postgres::Error),
46
47 #[error("Migration parse error: {0}")]
49 MigrationParseError(String),
50
51 #[error("Checksum mismatch for migration {script}: expected {expected}, found {found}")]
53 ChecksumMismatch {
54 script: String,
55 expected: i32,
56 found: i32,
57 },
58
59 #[error("Validation failed:\n{0}")]
61 ValidationFailed(String),
62
63 #[error("Migration failed for {script}: {reason}")]
65 MigrationFailed { script: String, reason: String },
66
67 #[error("Failed to acquire advisory lock: {0}")]
69 LockError(String),
70
71 #[error(
73 "Clean is disabled. Pass --allow-clean to enable it or set clean_enabled = true in config."
74 )]
75 CleanDisabled,
76
77 #[error("Baseline already exists. The schema history table is not empty.")]
79 BaselineExists,
80
81 #[error("IO error: {0}")]
83 IoError(#[from] std::io::Error),
84
85 #[error("Out-of-order migration not allowed: version {version} is below the highest applied version {highest}. Enable out_of_order to allow this.")]
87 OutOfOrder { version: String, highest: String },
88
89 #[error("Placeholder '{key}' not found. Available placeholders: {available}")]
91 PlaceholderNotFound { key: String, available: String },
92
93 #[error("Hook failed during {phase} ({script}): {reason}")]
95 HookFailed {
96 phase: String,
97 script: String,
98 reason: String,
99 },
100
101 #[error("Self-update failed: {0}")]
103 UpdateError(String),
104
105 #[error("Undo failed for {script}: {reason}")]
107 UndoFailed { script: String, reason: String },
108
109 #[error("No undo migration found for version {version}. Expected U{version}__*.sql file.")]
111 UndoMissing { version: String },
112
113 #[error("Lint found {error_count} error(s): {details}")]
115 LintFailed { error_count: usize, details: String },
116
117 #[error("Diff failed: {reason}")]
119 DiffFailed { reason: String },
120
121 #[error("Schema drift detected: {count} difference(s): {details}")]
123 DriftDetected { count: usize, details: String },
124
125 #[error("Snapshot error: {reason}")]
127 SnapshotError { reason: String },
128
129 #[error("Migration dependency cycle detected: {path}")]
131 DependencyCycle { path: String },
132
133 #[error("Migration V{version} depends on V{dependency}, which does not exist")]
135 MissingDependency { version: String, dependency: String },
136
137 #[error("Invalid directive in {script}: {reason}")]
139 InvalidDirective { script: String, reason: String },
140
141 #[error("Git error: {0}")]
143 GitError(String),
144
145 #[error("Migration conflicts detected: {count} conflict(s): {details}")]
147 ConflictsDetected { count: usize, details: String },
148
149 #[error("Database '{name}' not found. Available: {available}")]
151 DatabaseNotFound { name: String, available: String },
152
153 #[error("Multi-database dependency cycle: {path}")]
155 MultiDbDependencyCycle { path: String },
156
157 #[error("Multi-database error for '{name}': {reason}")]
159 MultiDbError { name: String, reason: String },
160
161 #[error("Pre-flight checks failed: {checks}")]
163 PreflightFailed { checks: String },
164
165 #[error("Guard {kind} failed for {script}: {expression}")]
167 GuardFailed {
168 kind: String,
169 script: String,
170 expression: String,
171 },
172
173 #[error("Migration blocked for {script}: {reason}. Use --force to override.")]
175 MigrationBlocked { script: String, reason: String },
176
177 #[error("Advisor error: {0}")]
179 AdvisorError(String),
180
181 #[error("Simulation failed: {reason}")]
183 SimulationFailed { reason: String },
184
185 #[error("Migration {script} contains non-transactional statement: {statement}. Remove --transaction or rewrite the migration.")]
187 NonTransactionalStatement { script: String, statement: String },
188
189 #[error("Connection lost during {operation}: {detail}")]
191 ConnectionLost { operation: String, detail: String },
192}
193
194pub type Result<T> = std::result::Result<T, WaypointError>;