scratch_server/errors/
api_error.rs1use std::fmt;
2use std::ops::{Deref, DerefMut};
3
4use crate::{http_parse_error::HttpParseError, Body, HttpResponse};
5
6#[derive(Debug)]
7pub struct ApiError(Box<ApiErrorImpl>);
8
9#[derive(Debug)]
10pub struct ApiErrorImpl {
11 pub error_response: HttpResponse,
12 pub method: Option<String>,
13 pub path: Option<String>,
14}
15
16impl Deref for ApiError {
17 type Target = ApiErrorImpl;
18
19 fn deref(&self) -> &Self::Target {
20 &self.0
21 }
22}
23
24impl DerefMut for ApiError {
25 fn deref_mut(&mut self) -> &mut Self::Target {
26 &mut self.0
27 }
28}
29
30impl ApiError {
31 pub fn new_with_html(code: u16, message: &str) -> Self {
32 ApiError(Box::new(ApiErrorImpl {
33 error_response: format_error(code, message),
34 method: None,
35 path: None,
36 }))
37 }
38
39 pub fn new_with_json(code: u16, message: &str) -> Self {
40 ApiError(Box::new(ApiErrorImpl {
41 error_response: HttpResponse::new(
42 Some(Body::Json(serde_json::json!({"message": message}))),
43 None,
44 code,
45 ),
46 method: None,
47 path: None,
48 }))
49 }
50
51 pub fn new_with_custom(response: HttpResponse) -> Self {
52 ApiError(Box::new(ApiErrorImpl {
53 error_response: response,
54 method: None,
55 path: None,
56 }))
57 }
58
59 pub fn into_response(self) -> HttpResponse {
60 self.0.error_response
61 }
62}
63
64impl fmt::Display for ApiError {
65 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66 write!(f, "{:?}", self)
67 }
68}
69
70impl std::error::Error for ApiError {}
71
72impl From<std::io::Error> for ApiError {
73 fn from(error: std::io::Error) -> Self {
74 ApiError::new_with_html(500, &format!("IO Error: {}", error))
75 }
76}
77
78impl From<Box<dyn std::error::Error>> for ApiError {
79 fn from(error: Box<dyn std::error::Error>) -> ApiError {
80 ApiError::new_with_json(500, &error.to_string())
81 }
82}
83
84impl From<serde_json::Error> for ApiError {
85 fn from(error: serde_json::Error) -> Self {
86 ApiError::new_with_json(400, &format!("JSON Serialization Error: {}", error))
87 }
88}
89
90impl From<&str> for ApiError {
91 fn from(error: &str) -> Self {
92 ApiError::new_with_json(400, error)
93 }
94}
95
96impl From<HttpParseError> for ApiError {
97 fn from(error: HttpParseError) -> Self {
98 ApiError::new_with_json(
99 400,
100 &format!("Error parsing HTTP request: {}", error.message),
101 )
102 }
103}
104
105fn format_error(error_code: u16, message: &str) -> HttpResponse {
106 let html = format!(
107 "<!DOCTYPE html>
108 <html lang=\"en\">
109 <head>
110 <meta charset=\"UTF-8\">
111 <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
112 <title>Error</title>
113 <style>
114 body {{
115 display: flex;
116 justify-content: center;
117 align-items: center;
118 height: 100vh;
119 font-family: Arial, sans-serif;
120 }}
121 .error-container {{
122 text-align: center;
123 }}
124 .error-container h1 {{
125 font-size: 3em;
126 color: #ff0000;
127 }}
128 .error-container p {{
129 font-size: 1.5em;
130 }}
131 </style>
132 </head>
133
134 <body>
135 <div class=\"error-container\">
136 <h1>{} {}</h1>
137 <p>{}</p>
138 </div>
139 </body>
140 </html>",
141 error_code,
142 get_cannonical_reason(error_code),
143 message
144 );
145 HttpResponse::new(
146 Some(Body::Text(html)),
147 Some(String::from("text/html")),
148 error_code,
149 )
150}
151
152fn get_cannonical_reason<'a>(status_code: u16) -> &'a str {
153 match status_code {
154 200 => "OK",
155 201 => "Created",
156 204 => "No Content",
157 301 => "Moved Permanently",
158 302 => "Found",
159 304 => "Not Modified",
160 400 => "Bad Request",
161 401 => "Unauthorized",
162 403 => "Forbidden",
163 404 => "Not Found",
164 405 => "Method Not Allowed",
165 500 => "Internal Server Error",
166 501 => "Not Implemented",
167 502 => "Bad Gateway",
168 503 => "Service Unavailable",
169 _ => "Unknown Status Code",
170 }
171}