Skip to main content

echoip/
error.rs

1use axum::http::StatusCode;
2use axum::response::{IntoResponse, Response};
3use serde::Serialize;
4
5#[derive(Serialize)]
6struct ErrorBody {
7    status: u16,
8    error: String,
9}
10
11pub struct AppError {
12    pub code: StatusCode,
13    pub message: String,
14    pub content_type: Option<String>,
15}
16
17impl AppError {
18    pub fn bad_request(message: impl Into<String>) -> Self {
19        Self {
20            code: StatusCode::BAD_REQUEST,
21            message: message.into(),
22            content_type: None,
23        }
24    }
25
26    pub fn not_found(message: impl Into<String>) -> Self {
27        Self {
28            code: StatusCode::NOT_FOUND,
29            message: message.into(),
30            content_type: None,
31        }
32    }
33
34    pub fn internal(message: impl Into<String>) -> Self {
35        Self {
36            code: StatusCode::INTERNAL_SERVER_ERROR,
37            message: message.into(),
38            content_type: None,
39        }
40    }
41
42    pub fn into_json(mut self) -> Self {
43        self.content_type = Some("application/json".to_string());
44        self
45    }
46}
47
48impl IntoResponse for AppError {
49    fn into_response(self) -> Response {
50        let body = if self.content_type.as_deref() == Some("application/json") {
51            let err_body = ErrorBody {
52                status: self.code.as_u16(),
53                error: self.message,
54            };
55            serde_json::to_string_pretty(&err_body).unwrap_or_default()
56        } else {
57            self.message
58        };
59
60        let content_type = self
61            .content_type
62            .unwrap_or_else(|| "text/plain".to_string());
63
64        (
65            self.code,
66            [(axum::http::header::CONTENT_TYPE, content_type)],
67            body,
68        )
69            .into_response()
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn test_bad_request() {
79        let err = AppError::bad_request("test error");
80        assert_eq!(err.code, StatusCode::BAD_REQUEST);
81        assert_eq!(err.message, "test error");
82        assert!(err.content_type.is_none());
83    }
84
85    #[test]
86    fn test_not_found() {
87        let err = AppError::not_found("not found");
88        assert_eq!(err.code, StatusCode::NOT_FOUND);
89        assert_eq!(err.message, "not found");
90    }
91
92    #[test]
93    fn test_internal() {
94        let err = AppError::internal("server error");
95        assert_eq!(err.code, StatusCode::INTERNAL_SERVER_ERROR);
96        assert_eq!(err.message, "server error");
97    }
98
99    #[test]
100    fn test_into_json() {
101        let err = AppError::bad_request("test").into_json();
102        assert_eq!(err.content_type.as_deref(), Some("application/json"));
103    }
104}