by_types/
api_error.rs

1use std::fmt::{Debug, Display};
2
3#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
4#[cfg_attr(feature = "server", derive(schemars::JsonSchema, aide::OperationIo))]
5#[serde(tag = "status_code", rename_all = "snake_case")]
6pub enum ApiError<T> {
7    BadRequest(T),
8    Unauthorized(T),
9    Forbidden(T),
10    NotFound(T),
11    InternalServerError(T),
12}
13
14impl<T> Display for ApiError<T> {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        match self {
17            ApiError::BadRequest(_) => write!(f, "Bad Request"),
18            ApiError::Unauthorized(_) => write!(f, "Unauthorized"),
19            ApiError::Forbidden(_) => write!(f, "Forbidden"),
20            ApiError::NotFound(_) => write!(f, "Not Found"),
21            ApiError::InternalServerError(_) => write!(f, "Internal Server Error"),
22        }
23    }
24}
25
26impl<T> std::error::Error for ApiError<T> where T: Debug {}
27
28impl<T> ApiError<T> {
29    pub fn into_inner(self) -> T {
30        match self {
31            ApiError::BadRequest(body) => body,
32            ApiError::Unauthorized(body) => body,
33            ApiError::Forbidden(body) => body,
34            ApiError::NotFound(body) => body,
35            ApiError::InternalServerError(body) => body,
36        }
37    }
38}
39
40#[cfg(feature = "server")]
41impl<T> axum::response::IntoResponse for ApiError<T>
42where
43    T: serde::Serialize,
44{
45    fn into_response(self) -> axum::response::Response {
46        let code = match self {
47            ApiError::BadRequest(_) => axum::http::StatusCode::BAD_REQUEST,
48            ApiError::Unauthorized(_) => axum::http::StatusCode::UNAUTHORIZED,
49            ApiError::Forbidden(_) => axum::http::StatusCode::FORBIDDEN,
50            ApiError::NotFound(_) => axum::http::StatusCode::NOT_FOUND,
51            ApiError::InternalServerError(_) => axum::http::StatusCode::INTERNAL_SERVER_ERROR,
52        };
53
54        (code, axum::Json(self)).into_response()
55    }
56}
57
58impl From<std::io::Error> for ApiError<Box<dyn std::error::Error>> {
59    fn from(error: std::io::Error) -> Self {
60        ApiError::InternalServerError(Box::new(error))
61    }
62}
63
64impl<T> From<reqwest::Error> for ApiError<T>
65where
66    T: From<String>,
67{
68    fn from(error: reqwest::Error) -> Self {
69        ApiError::InternalServerError(error.to_string().into())
70    }
71}
72
73#[cfg(feature = "server")]
74impl<T> From<sqlx::Error> for ApiError<T>
75where
76    T: From<String>,
77{
78    fn from(error: sqlx::Error) -> Self {
79        ApiError::InternalServerError(error.to_string().into())
80    }
81}
82
83impl<T> From<validator::ValidationError> for ApiError<T>
84where
85    T: From<String>,
86{
87    fn from(error: validator::ValidationError) -> Self {
88        ApiError::BadRequest(error.to_string().into())
89    }
90}