backend/
error.rs

1use std::{io, time::SystemTimeError};
2
3use axum::{
4    Json,
5    extract::{
6        multipart::MultipartError,
7        rejection::{JsonRejection, QueryRejection},
8    },
9    http::{
10        StatusCode,
11        header::{InvalidHeaderValue, ToStrError},
12    },
13    response::IntoResponse,
14};
15use bunny_api_tokio::error::Error as BunnyError;
16use deadpool::managed::{BuildError, PoolError};
17use diesel::{ConnectionError, result::Error as DieselError};
18use diesel_async::pooled_connection::PoolError as DieselPoolError;
19use lettre::{
20    address::AddressError, error::Error as EmailError, transport::smtp::Error as SmtpError,
21};
22use log::{debug, error};
23use redis::RedisError;
24use serde::Serialize;
25use serde_json::Error as JsonError;
26use thiserror::Error;
27use tokio::task::JoinError;
28use toml::de::Error as TomlError;
29
30#[derive(Debug, Error)]
31pub enum Error {
32    #[error(transparent)]
33    SqlError(#[from] DieselError),
34    #[error(transparent)]
35    PoolError(#[from] PoolError<DieselPoolError>),
36    #[error(transparent)]
37    BuildError(#[from] BuildError),
38    #[error(transparent)]
39    RedisError(#[from] RedisError),
40    #[error(transparent)]
41    ConnectionError(#[from] ConnectionError),
42    #[error(transparent)]
43    JoinError(#[from] JoinError),
44    #[error(transparent)]
45    IoError(#[from] io::Error),
46    #[error(transparent)]
47    TomlError(#[from] TomlError),
48    #[error(transparent)]
49    JsonError(#[from] JsonError),
50    #[error(transparent)]
51    SystemTimeError(#[from] SystemTimeError),
52    #[error(transparent)]
53    ToStrError(#[from] ToStrError),
54    #[error(transparent)]
55    RandomError(#[from] getrandom::Error),
56    #[error(transparent)]
57    BunnyError(#[from] BunnyError),
58    #[error(transparent)]
59    UrlParseError(#[from] url::ParseError),
60    #[error(transparent)]
61    JsonRejection(#[from] JsonRejection),
62    #[error(transparent)]
63    QueryRejection(#[from] QueryRejection),
64    #[error(transparent)]
65    MultipartError(#[from] MultipartError),
66    #[error(transparent)]
67    InvalidHeaderValue(#[from] InvalidHeaderValue),
68    #[error(transparent)]
69    EmailError(#[from] EmailError),
70    #[error(transparent)]
71    SmtpError(#[from] SmtpError),
72    #[error(transparent)]
73    SmtpAddressError(#[from] AddressError),
74    #[error("{0}")]
75    PasswordHashError(String),
76    #[error("{0}")]
77    BadRequest(String),
78    #[error("{0}")]
79    Unauthorized(String),
80    #[error("{0}")]
81    Forbidden(String),
82    #[error("{0}")]
83    TooManyRequests(String),
84    #[error("{0}")]
85    InternalServerError(String),
86    // TODO: remove when doing socket.io
87    #[error(transparent)]
88    AxumError(#[from] axum::Error),
89}
90
91impl IntoResponse for Error {
92    fn into_response(self) -> axum::response::Response {
93        let error = match self {
94            Error::SqlError(DieselError::NotFound) => {
95                (StatusCode::NOT_FOUND, Json(WebError::new(self.to_string())))
96            }
97            Error::BunnyError(BunnyError::NotFound(_)) => {
98                (StatusCode::NOT_FOUND, Json(WebError::new(self.to_string())))
99            }
100            Error::BadRequest(_) => (
101                StatusCode::BAD_REQUEST,
102                Json(WebError::new(self.to_string())),
103            ),
104            Error::Unauthorized(_) => (
105                StatusCode::UNAUTHORIZED,
106                Json(WebError::new(self.to_string())),
107            ),
108            Error::Forbidden(_) => (StatusCode::FORBIDDEN, Json(WebError::new(self.to_string()))),
109            Error::TooManyRequests(_) => (
110                StatusCode::TOO_MANY_REQUESTS,
111                Json(WebError::new(self.to_string())),
112            ),
113            _ => (
114                StatusCode::INTERNAL_SERVER_ERROR,
115                Json(WebError::new(self.to_string())),
116            ),
117        };
118
119        let (code, _) = error;
120
121        debug!("{self:?}");
122        error!("{code}: {self}");
123
124        error.into_response()
125    }
126}
127
128#[derive(Serialize)]
129struct WebError {
130    message: String,
131}
132
133impl WebError {
134    fn new(message: String) -> Self {
135        Self { message }
136    }
137}