Skip to main content

sark_core/error/
mod.rs

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 bad_request(message: impl Into<Cow<'static, str>>) -> Self {
59        Self::BadRequest(message.into())
60    }
61
62    pub fn invalid_integer_header() -> Self {
63        Self::BadRequest("Invalid integer header".into())
64    }
65
66    pub fn status_code(&self) -> StatusCode {
67        match self {
68            Self::NotFound => StatusCode::NOT_FOUND,
69            Self::MethodNotAllowed => StatusCode::METHOD_NOT_ALLOWED,
70            Self::BadRequest(_) => StatusCode::BAD_REQUEST,
71            Self::PayloadTooLarge(_) => StatusCode::PAYLOAD_TOO_LARGE,
72            Self::Unauthorized(_) => StatusCode::UNAUTHORIZED,
73            Self::Forbidden(_) => StatusCode::FORBIDDEN,
74            Self::InternalServerError(_) => StatusCode::INTERNAL_SERVER_ERROR,
75            Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
76            Self::Io(_) => StatusCode::INTERNAL_SERVER_ERROR,
77            Self::Http(_) => StatusCode::INTERNAL_SERVER_ERROR,
78            Self::Json(_) => StatusCode::BAD_REQUEST,
79            Self::HttpParse(_) => StatusCode::BAD_REQUEST,
80        }
81    }
82
83    pub fn to_response(&self) -> Response {
84        self.to_response_with_format(ErrorBodyFormat::PlainText)
85    }
86
87    pub fn to_response_with_format(&self, format: ErrorBodyFormat) -> Response {
88        let status = self.status_code();
89        match format {
90            ErrorBodyFormat::PlainText => Response::text_with_status(status, &self.to_string()),
91            ErrorBodyFormat::Json => {
92                let payload = serde_json::json!({
93                    "status": status.as_u16(),
94                    "error": self.to_string(),
95                });
96                match Response::json_with_status(status, &payload) {
97                    Ok(r) => r,
98                    Err(e) => Response::text_with_status(
99                        StatusCode::INTERNAL_SERVER_ERROR,
100                        &format!("Internal server error: {}", e),
101                    ),
102                }
103            }
104        }
105    }
106}
107
108impl From<&Error> for Response {
109    fn from(err: &Error) -> Self {
110        err.to_response()
111    }
112}
113
114impl From<Error> for Response {
115    fn from(err: Error) -> Self {
116        err.to_response()
117    }
118}