use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("configuration error: {0}")]
Config(String),
#[error("unauthorized: {0}")]
Unauthorized(String),
#[error("not found: {0}")]
NotFound(String),
#[error("bad request: {0}")]
BadRequest(String),
#[error("internal error: {0}")]
Internal(String),
#[cfg(feature = "storage")]
#[error("storage error: {0}")]
Storage(#[from] object_store::Error),
#[cfg(feature = "sql")]
#[error("sql error: {0}")]
Sql(#[from] sqlx::Error),
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("json error: {0}")]
Json(#[from] serde_json::Error),
}
impl IntoResponse for Error {
fn into_response(self) -> Response {
let (status, client_message, error_code) = match &self {
Error::Config(_) => {
tracing::error!(error = %self, "Configuration error");
(
StatusCode::INTERNAL_SERVER_ERROR,
"Internal server error",
"CONFIG_ERROR",
)
}
Error::Unauthorized(msg) => {
(StatusCode::UNAUTHORIZED, msg.as_str(), "UNAUTHORIZED")
}
Error::NotFound(msg) => {
(StatusCode::NOT_FOUND, msg.as_str(), "NOT_FOUND")
}
Error::BadRequest(msg) => {
(StatusCode::BAD_REQUEST, msg.as_str(), "BAD_REQUEST")
}
Error::Internal(_) => {
tracing::error!(error = %self, "Internal error");
(
StatusCode::INTERNAL_SERVER_ERROR,
"Internal server error",
"INTERNAL_ERROR",
)
}
#[cfg(feature = "storage")]
Error::Storage(_) => {
tracing::error!(error = %self, "Storage backend error");
(
StatusCode::INTERNAL_SERVER_ERROR,
"Storage operation failed",
"STORAGE_ERROR",
)
}
#[cfg(feature = "sql")]
Error::Sql(_) => {
tracing::error!(error = %self, "SQL error");
(
StatusCode::INTERNAL_SERVER_ERROR,
"Database operation failed",
"SQL_ERROR",
)
}
Error::Io(_) => {
tracing::error!(error = %self, "IO error");
(
StatusCode::INTERNAL_SERVER_ERROR,
"Internal server error",
"IO_ERROR",
)
}
Error::Json(_) => {
tracing::warn!(error = %self, "JSON parsing error");
(StatusCode::BAD_REQUEST, "Invalid JSON format", "JSON_ERROR")
}
};
let body = serde_json::json!({
"error": client_message,
"code": error_code,
"status": status.as_u16()
});
(status, axum::Json(body)).into_response()
}
}