Skip to main content

waypoint_core/
error.rs

1//! Error types for Waypoint operations.
2
3use thiserror::Error;
4
5/// Extract the full error message from a tokio_postgres::Error,
6/// including the underlying DbError details that Display hides.
7pub fn format_db_error(e: &tokio_postgres::Error) -> String {
8    // The source chain contains the actual DbError with message/detail/hint
9    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    // Fallback: walk the source chain
23    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/// All error types that Waypoint operations can produce.
33#[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
85/// Convenience type alias for `Result<T, WaypointError>`.
86pub type Result<T> = std::result::Result<T, WaypointError>;