use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use serde_json::json;
#[derive(Debug)]
pub enum AppError {
Database(sqlx::Error),
NotFound(String),
Conflict(String),
Unauthorized(String),
Forbidden(String),
BadRequest(String),
Internal(String),
}
impl std::fmt::Display for AppError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Database(e) => write!(f, "Database error: {e}"),
Self::NotFound(msg) => write!(f, "Not found: {msg}"),
Self::Conflict(msg) => write!(f, "Conflict: {msg}"),
Self::Unauthorized(msg) => write!(f, "Unauthorized: {msg}"),
Self::Forbidden(msg) => write!(f, "Forbidden: {msg}"),
Self::BadRequest(msg) => write!(f, "Bad request: {msg}"),
Self::Internal(msg) => write!(f, "Internal error: {msg}"),
}
}
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, message) = match &self {
Self::Database(e) => {
tracing::error!("Database error: {e}");
(
StatusCode::INTERNAL_SERVER_ERROR,
"Internal database error. Check server logs.",
)
}
Self::NotFound(msg) => (StatusCode::NOT_FOUND, msg.as_str()),
Self::Conflict(msg) => (StatusCode::CONFLICT, msg.as_str()),
Self::Unauthorized(msg) => (StatusCode::UNAUTHORIZED, msg.as_str()),
Self::Forbidden(msg) => (StatusCode::FORBIDDEN, msg.as_str()),
Self::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.as_str()),
Self::Internal(msg) => {
tracing::error!("Internal error: {msg}");
(
StatusCode::INTERNAL_SERVER_ERROR,
"Internal error. Check server logs.",
)
}
};
let body = axum::Json(json!({ "error": message }));
(status, body).into_response()
}
}
impl From<sqlx::Error> for AppError {
fn from(err: sqlx::Error) -> Self {
Self::Database(err)
}
}