use axum::{ Json, http::StatusCode, response::{IntoResponse, Response}, }; use tracing::error; use crate::models::ErrorResponse; #[derive(Debug)] pub enum AppError { Unauthorized, NotFound(String), BadRequest(String), TooManyRequests(String), /// (public_message, internal_details) — details are logged but not returned. Internal(String, Option), } impl IntoResponse for AppError { fn into_response(self) -> Response { let (status, error_message) = match self { AppError::Unauthorized => (StatusCode::UNAUTHORIZED, "Unauthorized".to_string()), AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg), AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg), AppError::TooManyRequests(msg) => (StatusCode::TOO_MANY_REQUESTS, msg), AppError::Internal(msg, details) => { if let Some(d) = details { error!("Internal error: {} — {}", msg, d); } else { error!("Internal error: {}", msg); } ( StatusCode::INTERNAL_SERVER_ERROR, "Internal error".to_string(), ) } }; let body = Json(ErrorResponse { error: error_message, }); (status, body).into_response() } } impl From for AppError where E: std::error::Error + Send + Sync + 'static, { fn from(err: E) -> Self { AppError::Internal("Internal Server Error".to_string(), Some(err.to_string())) } }