use axum::{ http::StatusCode, response::{IntoResponse, Response}, Json, }; use serde_json::json; use thiserror::Error; #[derive(Debug, Error)] pub enum AppError { #[error("not found: {0}")] NotFound(String), #[error("bad request: {0}")] BadRequest(String), #[error("conflict: {0}")] Conflict(String), #[error("database error: {0}")] Database(#[from] sqlx::Error), #[error("internal error: {0}")] Internal(#[from] anyhow::Error), #[error("io error: {0}")] Io(#[from] std::io::Error), #[error("json error: {0}")] Json(#[from] serde_json::Error), } impl IntoResponse for AppError { fn into_response(self) -> Response { let (status, message) = match &self { AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg.clone()), AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.clone()), AppError::Conflict(msg) => (StatusCode::CONFLICT, msg.clone()), AppError::Database(e) => { tracing::error!("Database error: {e}"); (StatusCode::INTERNAL_SERVER_ERROR, "database error".into()) } AppError::Internal(e) => { tracing::error!("Internal error: {e}"); ( StatusCode::INTERNAL_SERVER_ERROR, "internal server error".into(), ) } AppError::Io(e) => { tracing::error!("IO error: {e}"); (StatusCode::INTERNAL_SERVER_ERROR, "io error".into()) } AppError::Json(e) => (StatusCode::BAD_REQUEST, format!("json parse error: {e}")), }; let body = Json(json!({ "error": message })); (status, body).into_response() } } pub type AppResult = Result;