#[derive(Debug)]
pub enum DeploymentError {
ProcessFailed {
command: String,
exit_code: Option<i32>,
stdout: String,
stderr: String,
},
ConfigInvalid { file: String, reason: String },
NoProviderDetected { project_dir: String },
BuildFailed(String),
DeployRejected { reason: String },
StateFailed(String),
HttpError(foundation_netio::shared::http::HttpClientError),
IoError(std::io::Error),
SqliteError(String),
ExecutorError { reason: String },
Cloudflare {
status: u16,
message: String,
error_code: Option<String>,
},
Gcp { status: u16, message: String },
Aws {
status: u16,
message: String,
request_id: Option<String>,
},
Io {
path: String,
source: std::io::Error,
},
JsonInvalid { file: String, reason: String },
Generic(String),
}
impl std::error::Error for DeploymentError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::HttpError(e) => Some(e),
Self::IoError(e) => Some(e),
Self::Io { source, .. } => Some(source),
_ => None,
}
}
}
impl core::fmt::Display for DeploymentError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::ProcessFailed {
command,
exit_code,
stderr,
..
} => {
write!(
f,
"process '{command}' failed (exit {exit_code:?}): {stderr}"
)
}
Self::ConfigInvalid { file, reason } => {
write!(f, "invalid config '{file}': {reason}")
}
Self::NoProviderDetected { project_dir } => {
write!(f, "no deployment provider detected in '{project_dir}'")
}
Self::BuildFailed(msg) => write!(f, "build failed: {msg}"),
Self::DeployRejected { reason } => write!(f, "deploy rejected: {reason}"),
Self::StateFailed(msg) => write!(f, "state error: {msg}"),
Self::HttpError(err) => write!(f, "HTTP error: {err}"),
Self::IoError(err) => write!(f, "IO error: {err}"),
Self::SqliteError(msg) => write!(f, "SQLite error: {msg}"),
Self::ExecutorError { reason } => write!(f, "executor error: {reason}"),
Self::Cloudflare {
status, message, ..
} => {
write!(f, "Cloudflare API error ({status}): {message}")
}
Self::Gcp { status, message } => {
write!(f, "GCP API error ({status}): {message}")
}
Self::Aws {
status, message, ..
} => {
write!(f, "AWS API error ({status}): {message}")
}
Self::Io { path, source } => {
write!(f, "IO error at '{path}': {source}")
}
Self::JsonInvalid { file, reason } => {
write!(f, "JSON parse error in '{file}': {reason}")
}
Self::Generic(msg) => write!(f, "{msg}"),
}
}
}
impl From<std::io::Error> for DeploymentError {
fn from(err: std::io::Error) -> Self {
Self::IoError(err)
}
}
impl From<foundation_netio::shared::http::HttpClientError> for DeploymentError {
fn from(err: foundation_netio::shared::http::HttpClientError) -> Self {
Self::HttpError(err)
}
}
impl From<serde_json::Error> for DeploymentError {
fn from(err: serde_json::Error) -> Self {
Self::JsonInvalid {
file: "unknown".to_string(),
reason: err.to_string(),
}
}
}
impl From<foundation_db::StorageError> for DeploymentError {
fn from(err: foundation_db::StorageError) -> Self {
Self::StateFailed(err.to_string())
}
}
impl DeploymentError {
pub fn generic(msg: impl Into<String>) -> Self {
Self::Generic(msg.into())
}
}