1use thiserror::Error;
2
3pub fn format_db_error(e: &tokio_postgres::Error) -> String {
6 if let Some(db_err) = e.as_db_error() {
8 let mut msg = db_err.message().to_string();
9 if let Some(detail) = db_err.detail() {
10 msg.push_str(&format!("\n Detail: {}", detail));
11 }
12 if let Some(hint) = db_err.hint() {
13 msg.push_str(&format!("\n Hint: {}", hint));
14 }
15 if let Some(position) = db_err.position() {
16 msg.push_str(&format!("\n Position: {:?}", position));
17 }
18 return msg;
19 }
20 let mut msg = e.to_string();
22 let mut source = std::error::Error::source(e);
23 while let Some(s) = source {
24 msg.push_str(&format!(": {}", s));
25 source = s.source();
26 }
27 msg
28}
29
30#[derive(Error, Debug)]
31pub enum WaypointError {
32 #[error("Configuration error: {0}")]
33 ConfigError(String),
34
35 #[error("Database error: {}", format_db_error(.0))]
36 DatabaseError(#[from] tokio_postgres::Error),
37
38 #[error("Migration parse error: {0}")]
39 MigrationParseError(String),
40
41 #[error("Checksum mismatch for migration {script}: expected {expected}, found {found}")]
42 ChecksumMismatch {
43 script: String,
44 expected: i32,
45 found: i32,
46 },
47
48 #[error("Validation failed:\n{0}")]
49 ValidationFailed(String),
50
51 #[error("Migration failed for {script}: {reason}")]
52 MigrationFailed { script: String, reason: String },
53
54 #[error("Failed to acquire advisory lock: {0}")]
55 LockError(String),
56
57 #[error(
58 "Clean is disabled. Pass --allow-clean to enable it or set clean_enabled = true in config."
59 )]
60 CleanDisabled,
61
62 #[error("Baseline already exists. The schema history table is not empty.")]
63 BaselineExists,
64
65 #[error("IO error: {0}")]
66 IoError(#[from] std::io::Error),
67
68 #[error("Out-of-order migration not allowed: version {version} is below the highest applied version {highest}. Enable out_of_order to allow this.")]
69 OutOfOrder { version: String, highest: String },
70
71 #[error("Placeholder '{key}' not found. Available placeholders: {available}")]
72 PlaceholderNotFound { key: String, available: String },
73
74 #[error("Hook failed during {phase} ({script}): {reason}")]
75 HookFailed {
76 phase: String,
77 script: String,
78 reason: String,
79 },
80}
81
82pub type Result<T> = std::result::Result<T, WaypointError>;