whynot_errors/
app_error.rs1use std::fmt::Display;
2
3use axum::http::StatusCode;
4use axum::response::{Html, IntoResponse, Response};
5use axum::Json;
6use tracing::{error, warn};
7
8#[derive(Debug)]
11pub struct AppError {
12 pub code: StatusCode,
13 pub message: String,
14}
15
16impl Display for AppError {
17 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18 write!(f, "Code: {}; {};", self.code.as_u16(), self.message)
19 }
20}
21
22impl AppError {
23 pub fn new(obj: impl ToString) -> Self {
26 error!("Server Error {}", obj.to_string());
27
28 Self {
29 code: StatusCode::INTERNAL_SERVER_ERROR,
30 message: obj.to_string(),
31 }
32 }
33
34 #[deprecated]
36 pub fn from(obj: impl ToString) -> Self {
37 Self {
38 code: StatusCode::INTERNAL_SERVER_ERROR,
39 message: obj.to_string(),
40 }
41 }
42
43 pub fn code<T: ToString>(code: StatusCode) -> impl Fn(T) -> Self {
45 move |obj| {
46 warn!(code = code.as_u16(), message = obj.to_string(), "Error");
47
48 Self {
49 code,
50 message: obj.to_string(),
51 }
52 }
53 }
54}
55
56impl IntoResponse for AppError {
57 fn into_response(self) -> Response {
58 (self.code, self.message).into_response()
59 }
60}
61
62pub type AppResult<T> = Result<T, AppError>;
64
65pub type JsonResult<T> = AppResult<Json<T>>;
67
68pub fn json_ok<T>(obj: T) -> JsonResult<T> {
70 Ok(Json(obj))
71}
72
73pub type HtmlResult = AppResult<Html<String>>;
75
76pub fn html_ok(s: impl ToString) -> HtmlResult {
78 Ok(Html(s.to_string()))
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn test_fmt() {
87 let err = AppError {
88 code: StatusCode::OK,
89 message: "ok".to_string(),
90 };
91
92 assert_eq!(err.to_string(), "Code: 200; ok;");
93 }
94
95 #[test]
97 fn test_from() {
98 let err2: AppError = AppError::new("hi");
99
100 assert_eq!(err2.message, "hi");
101 assert_eq!(err2.code, StatusCode::INTERNAL_SERVER_ERROR);
102 }
103
104 #[test]
106 fn test_json() {
107 let resp: JsonResult<String> = json_ok("hi".to_string());
108 assert_eq!(resp.unwrap().to_string(), "hi");
109 }
110
111 #[test]
112 fn test_traits() {
113 assert_eq!(AppError::new("hi").message, "hi");
114 assert_eq!(AppError::new("hi".to_string()).message, "hi");
115 }
116
117 #[test]
118 fn test_code() {
119 let r: Result<(), String> = Err("hi".to_string());
120 let mapped = r.map_err(AppError::code(StatusCode::METHOD_NOT_ALLOWED));
121
122 assert!(mapped.is_err());
123
124 let e = mapped.unwrap_err();
125
126 assert_eq!(e.code, StatusCode::METHOD_NOT_ALLOWED);
127 assert_eq!(e.message, "hi");
128 }
129}