use axum::{ Json, http::StatusCode, response::{IntoResponse, Response}, }; use serde_json::json; use thiserror::Error; pub type Result = std::result::Result; #[derive(Error, Debug)] pub enum AppError { #[error("Not Found: {0}")] NotFound(String), #[error("Forbidden: {0}")] Forbidden(String), #[error("Internal Server Error: {0}")] Internal(String), #[error("Bad Request: {0}")] BadRequest(String), #[error("Serialization Error: {0}")] JsonError(#[from] serde_json::Error), #[error("I/O Error: {0}")] IoError(#[from] std::io::Error), } impl From for String { fn from(err: AppError) -> String { err.to_string() } } impl IntoResponse for AppError { fn into_response(self) -> Response { let (status, error_message) = match &self { AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg.clone()), AppError::Forbidden(msg) => (StatusCode::FORBIDDEN, msg.clone()), AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.clone()), AppError::Internal(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg.clone()), AppError::JsonError(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("JSON Error: {}", e)), AppError::IoError(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("I/O Error: {}", e)), }; let body = Json(json!({ "error": error_message, })); (status, body).into_response() } } #[cfg(test)] mod tests { use super::*; use axum::http::StatusCode; use axum::response::IntoResponse; #[test] fn test_app_error_not_found() { let err = AppError::NotFound("item not found".into()); let res = err.into_response(); assert_eq!(res.status(), StatusCode::NOT_FOUND); } #[test] fn test_app_error_forbidden() { let err = AppError::Forbidden("access denied".into()); let res = err.into_response(); assert_eq!(res.status(), StatusCode::FORBIDDEN); } #[test] fn test_app_error_internal() { let err = AppError::Internal("db error".into()); let res = err.into_response(); assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR); } #[test] fn test_app_error_bad_request() { let err = AppError::BadRequest("invalid json".into()); let res = err.into_response(); assert_eq!(res.status(), StatusCode::BAD_REQUEST); } #[test] fn test_app_error_display() { let err = AppError::NotFound("test".into()); assert_eq!(err.to_string(), "Not Found: test"); let err = AppError::Forbidden("test".into()); assert_eq!(err.to_string(), "Forbidden: test"); let err = AppError::Internal("test".into()); assert_eq!(err.to_string(), "Internal Server Error: test"); let err = AppError::BadRequest("test".into()); assert_eq!(err.to_string(), "Bad Request: test"); } } impl From for AppError { fn from(msg: String) -> Self { AppError::Internal(msg) } } impl From<&str> for AppError { fn from(msg: &str) -> Self { AppError::Internal(msg.to_string()) } }