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 #[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}