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 msg
30}
31
32#[derive(Error, Debug)]
34pub enum WaypointError {
35 #[error("Configuration error: {0}")]
36 ConfigError(String),
37
38 #[error("Database error: {}", format_db_error(.0))]
39 DatabaseError(#[from] tokio_postgres::Error),
40
41 #[error("Migration parse error: {0}")]
42 MigrationParseError(String),
43
44 #[error("Checksum mismatch for migration {script}: expected {expected}, found {found}")]
45 ChecksumMismatch {
46 script: String,
47 expected: i32,
48 found: i32,
49 },
50
51 #[error("Validation failed:\n{0}")]
52 ValidationFailed(String),
53
54 #[error("Migration failed for {script}: {reason}")]
55 MigrationFailed { script: String, reason: String },
56
57 #[error("Failed to acquire advisory lock: {0}")]
58 LockError(String),
59
60 #[error(
61 "Clean is disabled. Pass --allow-clean to enable it or set clean_enabled = true in config."
62 )]
63 CleanDisabled,
64
65 #[error("Baseline already exists. The schema history table is not empty.")]
66 BaselineExists,
67
68 #[error("IO error: {0}")]
69 IoError(#[from] std::io::Error),
70
71 #[error("Out-of-order migration not allowed: version {version} is below the highest applied version {highest}. Enable out_of_order to allow this.")]
72 OutOfOrder { version: String, highest: String },
73
74 #[error("Placeholder '{key}' not found. Available placeholders: {available}")]
75 PlaceholderNotFound { key: String, available: String },
76
77 #[error("Hook failed during {phase} ({script}): {reason}")]
78 HookFailed {
79 phase: String,
80 script: String,
81 reason: String,
82 },
83}
84
85pub type Result<T> = std::result::Result<T, WaypointError>;