use arcbox_error::CommonError;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use serde::Serialize;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, DockerError>;
#[derive(Debug, Error)]
pub enum DockerError {
#[error(transparent)]
Common(#[from] CommonError),
#[error("No such container: {0}")]
ContainerNotFound(String),
#[error("No such image: {0}")]
ImageNotFound(String),
#[error("No such volume: {0}")]
VolumeNotFound(String),
#[error("No such network: {0}")]
NetworkNotFound(String),
#[error("No such exec instance: {0}")]
ExecNotFound(String),
#[error("Invalid parameter: {0}")]
InvalidParameter(String),
#[error("Bad request: {0}")]
BadRequest(String),
#[error("Conflict: {0}")]
Conflict(String),
#[error("Server error: {0}")]
Server(String),
#[error("Not implemented: {0}")]
NotImplemented(String),
#[error("Context error: {0}")]
Context(String),
}
impl DockerError {
#[must_use]
pub const fn status_code(&self) -> StatusCode {
match self {
Self::Common(e) => Self::common_status_code(e),
Self::ContainerNotFound(_)
| Self::ImageNotFound(_)
| Self::VolumeNotFound(_)
| Self::NetworkNotFound(_)
| Self::ExecNotFound(_) => StatusCode::NOT_FOUND,
Self::InvalidParameter(_) | Self::BadRequest(_) => StatusCode::BAD_REQUEST,
Self::Conflict(_) => StatusCode::CONFLICT,
Self::Server(_) | Self::Context(_) => StatusCode::INTERNAL_SERVER_ERROR,
Self::NotImplemented(_) => StatusCode::NOT_IMPLEMENTED,
}
}
const fn common_status_code(err: &CommonError) -> StatusCode {
match err {
CommonError::Config(_) | CommonError::InvalidState(_) => StatusCode::BAD_REQUEST,
CommonError::NotFound(_) => StatusCode::NOT_FOUND,
CommonError::AlreadyExists(_) => StatusCode::CONFLICT,
CommonError::Timeout(_) => StatusCode::GATEWAY_TIMEOUT,
CommonError::PermissionDenied(_) => StatusCode::FORBIDDEN,
CommonError::Io(_) | CommonError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
impl From<std::io::Error> for DockerError {
fn from(err: std::io::Error) -> Self {
Self::Common(CommonError::from(err))
}
}
#[derive(Debug, Serialize)]
pub struct ErrorResponse {
pub message: String,
}
impl IntoResponse for DockerError {
fn into_response(self) -> Response {
let status = self.status_code();
let body = serde_json::json!({
"message": self.to_string()
});
(status, axum::Json(body)).into_response()
}
}