Skip to main content

scratch_server/errors/
api_error.rs

1use 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}