use axum::response::{IntoResponse, Response};
use axum::http::StatusCode;
use axum::Json;
use serde_json::json;
#[derive(Debug)]
pub enum ActionError {
BadRequest(String),
Unauthorized,
Forbidden,
NotFound,
Conflict(String),
Internal(String),
}
impl std::fmt::Display for ActionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ActionError::BadRequest(msg) => write!(f, "bad request: {msg}"),
ActionError::Unauthorized => f.write_str("unauthorized"),
ActionError::Forbidden => f.write_str("forbidden"),
ActionError::NotFound => f.write_str("not found"),
ActionError::Conflict(msg) => write!(f, "conflict: {msg}"),
ActionError::Internal(msg) => write!(f, "internal error: {msg}"),
}
}
}
impl std::error::Error for ActionError {}
impl IntoResponse for ActionError {
fn into_response(self) -> Response {
let (status, code, message) = match &self {
ActionError::BadRequest(msg) => (StatusCode::BAD_REQUEST, "BAD_REQUEST", msg.as_str()),
ActionError::Unauthorized => (StatusCode::UNAUTHORIZED, "UNAUTHORIZED", "unauthorized"),
ActionError::Forbidden => (StatusCode::FORBIDDEN, "FORBIDDEN", "forbidden"),
ActionError::NotFound => (StatusCode::NOT_FOUND, "NOT_FOUND", "not found"),
ActionError::Conflict(msg) => (StatusCode::CONFLICT, "CONFLICT", msg.as_str()),
ActionError::Internal(msg) => {
tracing::error!(error = %msg, "action internal error");
(StatusCode::INTERNAL_SERVER_ERROR, "INTERNAL", "internal server error")
}
};
(status, Json(json!({ "error": message, "code": code }))).into_response()
}
}