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}