1use axum::{
2 http::StatusCode,
3 response::{IntoResponse, Response},
4 Json,
5};
6use serde::Serialize;
7use thiserror::Error;
8
9#[derive(Debug, Error)]
11pub enum AppError {
12 #[error("Not Found: {0}")]
13 NotFound(String),
14
15 #[error("Validation Error: {0}")]
16 ValidationError(String),
17
18 #[error("Unauthorized: {0}")]
19 Unauthorized(String),
20
21 #[error("Internal Server Error")]
22 InternalServerError(#[source] Box<dyn std::error::Error + Send + Sync>),
23
24 #[error("Database Error: {0}")]
25 DatabaseError(String),
26}
27
28#[derive(Serialize)]
30pub struct ErrorResponse {
31 pub status: u16,
32 pub message: String,
33}
34
35impl IntoResponse for AppError {
36 fn into_response(self) -> Response {
37 let (status, message) = match &self {
38 AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg.clone()),
39 AppError::ValidationError(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
40 AppError::Unauthorized(msg) => (StatusCode::UNAUTHORIZED, msg.clone()),
41 AppError::InternalServerError(err) => {
42 eprintln!("Internal Server Error: {}", err);
44 (
45 StatusCode::INTERNAL_SERVER_ERROR,
46 "Internal Server Error".to_string(),
47 )
48 }
49 AppError::DatabaseError(msg) => {
50 eprintln!("Database Error: {}", msg);
51 (
52 StatusCode::INTERNAL_SERVER_ERROR,
53 "Database Error".to_string(),
54 )
55 }
56 };
57
58 let body = Json(ErrorResponse {
59 status: status.as_u16(),
60 message,
61 });
62
63 (status, body).into_response()
64 }
65}
66
67pub fn setup() {
68 println!("ferrox-errors initialized: Provides global AppError and IntoResponse for Axum.");
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use axum::response::IntoResponse;
75 use axum::http::StatusCode;
76
77 #[test]
78 fn test_error_formatting() {
79 let err = AppError::NotFound("User".into());
80 assert_eq!(err.to_string(), "Not Found: User");
81
82 let err = AppError::ValidationError("Invalid email".into());
83 assert_eq!(err.to_string(), "Validation Error: Invalid email");
84 }
85
86 #[test]
87 fn test_into_response() {
88 let err = AppError::Unauthorized("Invalid token".into());
89 let response = err.into_response();
90 assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
91
92 let err = AppError::DatabaseError("Connection lost".into());
93 let response = err.into_response();
94 assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
95 }
96}