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}