1use std::borrow::Cow;
2use std::io;
3
4use http::StatusCode;
5use thiserror::Error;
6
7use crate::http::Response;
8
9pub type Result<T> = std::result::Result<T, Error>;
10
11#[derive(Error, Debug)]
12pub enum Error {
13 #[error("Not found")]
14 NotFound,
15
16 #[error("Method not allowed")]
17 MethodNotAllowed,
18
19 #[error("Bad request: {0}")]
20 BadRequest(Cow<'static, str>),
21
22 #[error("Payload too large: {0}")]
23 PayloadTooLarge(Cow<'static, str>),
24
25 #[error("Unauthorized: {0}")]
26 Unauthorized(Cow<'static, str>),
27
28 #[error("Forbidden: {0}")]
29 Forbidden(Cow<'static, str>),
30
31 #[error("Internal server error: {0}")]
32 InternalServerError(Cow<'static, str>),
33
34 #[error("Internal error: {0}")]
35 Internal(Cow<'static, str>),
36
37 #[error("IO error: {0}")]
38 Io(#[from] io::Error),
39
40 #[error("HTTP error: {0}")]
41 Http(#[from] http::Error),
42
43 #[error("JSON error: {0}")]
44 Json(#[from] serde_json::Error),
45
46 #[error("HTTP parse error: {0}")]
47 HttpParse(#[from] httparse::Error),
48}
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
51pub enum ErrorBodyFormat {
52 #[default]
53 PlainText,
54 Json,
55}
56
57impl Error {
58 pub fn invalid_integer_header() -> Self {
59 Self::BadRequest("Invalid integer header".into())
60 }
61
62 pub fn status_code(&self) -> StatusCode {
63 match self {
64 Self::NotFound => StatusCode::NOT_FOUND,
65 Self::MethodNotAllowed => StatusCode::METHOD_NOT_ALLOWED,
66 Self::BadRequest(_) => StatusCode::BAD_REQUEST,
67 Self::PayloadTooLarge(_) => StatusCode::PAYLOAD_TOO_LARGE,
68 Self::Unauthorized(_) => StatusCode::UNAUTHORIZED,
69 Self::Forbidden(_) => StatusCode::FORBIDDEN,
70 Self::InternalServerError(_) => StatusCode::INTERNAL_SERVER_ERROR,
71 Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
72 Self::Io(_) => StatusCode::INTERNAL_SERVER_ERROR,
73 Self::Http(_) => StatusCode::INTERNAL_SERVER_ERROR,
74 Self::Json(_) => StatusCode::BAD_REQUEST,
75 Self::HttpParse(_) => StatusCode::BAD_REQUEST,
76 }
77 }
78
79 pub fn to_response(&self) -> Response {
80 self.to_response_with_format(ErrorBodyFormat::PlainText)
81 }
82
83 pub fn to_response_with_format(&self, format: ErrorBodyFormat) -> Response {
84 let status = self.status_code();
85 match format {
86 ErrorBodyFormat::PlainText => Response::text_with_status(status, &self.to_string()),
87 ErrorBodyFormat::Json => {
88 let payload = serde_json::json!({
89 "status": status.as_u16(),
90 "error": self.to_string(),
91 });
92 match Response::json_with_status(status, &payload) {
93 Ok(r) => r,
94 Err(e) => Response::text_with_status(
95 StatusCode::INTERNAL_SERVER_ERROR,
96 &format!("Internal server error: {}", e),
97 ),
98 }
99 }
100 }
101 }
102}
103
104impl From<&Error> for Response {
105 fn from(err: &Error) -> Self {
106 err.to_response()
107 }
108}
109
110impl From<Error> for Response {
111 fn from(err: Error) -> Self {
112 err.to_response()
113 }
114}
115
116#[cfg(test)]
117mod tests;