Skip to main content

waypoint_core/
error.rs

1use thiserror::Error;
2
3/// Extract the full error message from a tokio_postgres::Error,
4/// including the underlying DbError details that Display hides.
5pub fn format_db_error(e: &tokio_postgres::Error) -> String {
6    // The source chain contains the actual DbError with message/detail/hint
7    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    // Fallback: walk the source chain
21    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>;