elif_http/errors/
responses.rs1use super::HttpError;
4use crate::response::{ElifResponse, ElifStatusCode, IntoElifResponse};
5use axum::response::{IntoResponse, Response};
6use axum::Json;
7use serde_json::json;
8
9impl HttpError {
10 pub fn status_code(&self) -> ElifStatusCode {
12 match self {
13 HttpError::StartupFailed { .. } => ElifStatusCode::INTERNAL_SERVER_ERROR,
14 HttpError::ShutdownFailed { .. } => ElifStatusCode::INTERNAL_SERVER_ERROR,
15 HttpError::ConfigError { .. } => ElifStatusCode::INTERNAL_SERVER_ERROR,
16 HttpError::ServiceResolutionFailed { .. } => ElifStatusCode::INTERNAL_SERVER_ERROR,
17 HttpError::RequestTimeout => ElifStatusCode::REQUEST_TIMEOUT,
18 HttpError::RequestTooLarge { .. } => ElifStatusCode::PAYLOAD_TOO_LARGE,
19 HttpError::BadRequest { .. } => ElifStatusCode::BAD_REQUEST,
20 HttpError::InternalError { .. } => ElifStatusCode::INTERNAL_SERVER_ERROR,
21 HttpError::HealthCheckFailed { .. } => ElifStatusCode::SERVICE_UNAVAILABLE,
22 HttpError::DatabaseError { .. } => ElifStatusCode::INTERNAL_SERVER_ERROR,
23 HttpError::ValidationError { .. } => ElifStatusCode::UNPROCESSABLE_ENTITY,
24 HttpError::NotFound { .. } => ElifStatusCode::NOT_FOUND,
25 HttpError::Conflict { .. } => ElifStatusCode::CONFLICT,
26 HttpError::Unauthorized => ElifStatusCode::UNAUTHORIZED,
27 HttpError::Forbidden { .. } => ElifStatusCode::FORBIDDEN,
28 }
29 }
30
31 pub fn error_hint(&self) -> Option<&'static str> {
33 match &self {
34 HttpError::RequestTooLarge { .. } => Some("Reduce request payload size"),
35 HttpError::RequestTimeout => Some("Retry the request"),
36 HttpError::BadRequest { .. } => Some("Check request format and parameters"),
37 HttpError::HealthCheckFailed { .. } => {
38 Some("Server may be starting up or experiencing issues")
39 }
40 _ => None,
41 }
42 }
43}
44
45impl IntoElifResponse for HttpError {
47 fn into_response(self) -> ElifResponse {
48 let body = json!({
49 "error": {
50 "code": self.error_code(),
51 "message": self.to_string(),
52 "hint": self.error_hint()
53 }
54 });
55
56 ElifResponse::with_status(self.status_code()).json_value(body)
57 }
58}
59
60impl IntoResponse for HttpError {
62 fn into_response(self) -> Response {
63 let status = self.status_code().to_axum(); let body = json!({
65 "error": {
66 "code": self.error_code(),
67 "message": self.to_string(),
68 "hint": self.error_hint()
69 }
70 });
71
72 (status, Json(body)).into_response()
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn test_error_status_codes() {
82 assert_eq!(
83 HttpError::bad_request("test").status_code(),
84 crate::response::status::ElifStatusCode::BAD_REQUEST
85 );
86 assert_eq!(
87 HttpError::RequestTimeout.status_code(),
88 crate::response::status::ElifStatusCode::REQUEST_TIMEOUT
89 );
90 assert_eq!(
91 HttpError::RequestTooLarge {
92 size: 100,
93 limit: 50
94 }
95 .status_code(),
96 crate::response::status::ElifStatusCode::PAYLOAD_TOO_LARGE
97 );
98 assert_eq!(
99 HttpError::health_check("Database unavailable").status_code(),
100 crate::response::status::ElifStatusCode::SERVICE_UNAVAILABLE
101 );
102 }
103
104 #[test]
105 fn test_validation_error_status_code() {
106 let validation_error = HttpError::validation_error("Field is required");
107 assert_eq!(
108 validation_error.status_code(),
109 crate::response::status::ElifStatusCode::UNPROCESSABLE_ENTITY
110 );
111 }
112
113 #[test]
114 fn test_error_hints() {
115 let timeout_error = HttpError::RequestTimeout;
116 assert_eq!(timeout_error.error_hint(), Some("Retry the request"));
117
118 let large_request_error = HttpError::RequestTooLarge {
119 size: 100,
120 limit: 50,
121 };
122 assert_eq!(
123 large_request_error.error_hint(),
124 Some("Reduce request payload size")
125 );
126
127 let not_found_error = HttpError::not_found("User");
128 assert_eq!(not_found_error.error_hint(), None);
129 }
130
131 #[test]
132 fn test_error_response_format_consistency() {
133 use axum::response::IntoResponse as AxumIntoResponse;
134
135 let error = HttpError::not_found("User");
136 let response = AxumIntoResponse::into_response(error);
137
138 assert_eq!(response.status(), axum::http::StatusCode::NOT_FOUND);
139 }
140}