1use std::convert::From;
7
8use actix::MailboxError;
9use actix_web::{
10 error::ResponseError,
11 http::{header, StatusCode},
12 HttpResponse, HttpResponseBuilder,
13};
14use argon2_creds::errors::CredsError;
15use db_core::errors::DBError;
16use derive_more::{Display, Error};
17use lettre::transport::smtp::Error as SmtpError;
18use libmcaptcha::errors::CaptchaError;
19use serde::{Deserialize, Serialize};
20use tokio::sync::oneshot::error::RecvError;
21use url::ParseError;
22use validator::ValidationErrors;
23
24#[derive(Debug, Display, Error)]
25pub struct SmtpErrorWrapper(SmtpError);
26
27#[derive(Debug, Display, Error)]
28pub struct DBErrorWrapper(DBError);
29
30impl std::cmp::PartialEq for DBErrorWrapper {
31 fn eq(&self, other: &Self) -> bool {
32 format!("{}", self.0) == format!("{}", other.0)
33 }
34}
35
36impl std::cmp::PartialEq for SmtpErrorWrapper {
37 fn eq(&self, other: &Self) -> bool {
38 self.0.status() == other.0.status()
39 }
40}
41
42#[derive(Debug, Display, PartialEq, Error)]
43pub enum ServiceError {
44 #[display(fmt = "internal server error")]
45 InternalServerError,
46
47 #[display(
48 fmt = "This server is is closed for registration. Contact admin if this is unexpecter"
49 )]
50 ClosedForRegistration,
51
52 #[display(fmt = "The value you entered for email is not an email")] NotAnEmail,
54 #[display(fmt = "The value you entered for URL is not a URL")] NotAUrl,
56
57 #[display(fmt = "Wrong password")]
58 WrongPassword,
59 #[display(fmt = "Username not found")]
60 UsernameNotFound,
61 #[display(fmt = "Account not found")]
62 AccountNotFound,
63
64 #[display(fmt = "Can't allow profanity in usernames")]
66 ProfainityError,
67 #[display(fmt = "Username contains blacklisted words")]
70 BlacklistError,
71 #[display(fmt = "username_case_mapped violation")]
75 UsernameCaseMappedError,
76
77 #[display(fmt = "Passsword too short")]
78 PasswordTooShort,
79 #[display(fmt = "Username too long")]
80 PasswordTooLong,
81 #[display(fmt = "Passwords don't match")]
82 PasswordsDontMatch,
83
84 #[display(fmt = "Username not available")]
86 UsernameTaken,
87
88 #[display(fmt = "Email not available")]
90 EmailTaken,
91
92 #[display(fmt = "Unable to send email, contact admin")]
94 UnableToSendEmail(SmtpErrorWrapper),
95
96 #[display(fmt = "Token not found. Is token registered?")]
98 TokenNotFound,
99
100 #[display(fmt = "{}", _0)]
101 CaptchaError(CaptchaError),
102
103 #[display(fmt = "{}", _0)]
104 DBError(DBErrorWrapper),
105
106 #[display(fmt = "Captcha not found.")]
108 CaptchaNotFound,
109
110 #[display(fmt = "Traffic pattern not found")]
112 TrafficPatternNotFound,
113}
114
115#[derive(Serialize, Deserialize)]
116pub struct ErrorToResponse {
117 pub error: String,
118}
119
120impl ResponseError for ServiceError {
121 fn error_response(&self) -> HttpResponse {
122 HttpResponseBuilder::new(self.status_code())
123 .append_header((header::CONTENT_TYPE, "application/json; charset=UTF-8"))
124 .body(
125 serde_json::to_string(&ErrorToResponse {
126 error: self.to_string(),
127 })
128 .unwrap(),
129 )
130 }
131
132 fn status_code(&self) -> StatusCode {
133 match self {
134 ServiceError::ClosedForRegistration => StatusCode::FORBIDDEN,
135 ServiceError::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR,
136 ServiceError::NotAnEmail => StatusCode::BAD_REQUEST,
137 ServiceError::NotAUrl => StatusCode::BAD_REQUEST,
138 ServiceError::WrongPassword => StatusCode::UNAUTHORIZED,
139 ServiceError::UsernameNotFound => StatusCode::NOT_FOUND,
140 ServiceError::AccountNotFound => StatusCode::NOT_FOUND,
141
142 ServiceError::ProfainityError => StatusCode::BAD_REQUEST,
143 ServiceError::BlacklistError => StatusCode::BAD_REQUEST,
144 ServiceError::UsernameCaseMappedError => StatusCode::BAD_REQUEST,
145
146 ServiceError::PasswordTooShort => StatusCode::BAD_REQUEST,
147 ServiceError::PasswordTooLong => StatusCode::BAD_REQUEST,
148 ServiceError::PasswordsDontMatch => StatusCode::BAD_REQUEST,
149
150 ServiceError::UsernameTaken => StatusCode::BAD_REQUEST,
151 ServiceError::EmailTaken => StatusCode::BAD_REQUEST,
152
153 ServiceError::TokenNotFound => StatusCode::NOT_FOUND,
154 ServiceError::CaptchaError(e) => {
155 log::error!("{}", e);
156 match e {
157 CaptchaError::MailboxError => StatusCode::INTERNAL_SERVER_ERROR,
158 _ => StatusCode::BAD_REQUEST,
159 }
160 }
161
162 ServiceError::UnableToSendEmail(e) => {
163 log::error!("{}", e.0);
164 StatusCode::INTERNAL_SERVER_ERROR
165 }
166
167 ServiceError::DBError(_) => StatusCode::INTERNAL_SERVER_ERROR,
168 ServiceError::CaptchaNotFound => StatusCode::NOT_FOUND,
169 ServiceError::TrafficPatternNotFound => StatusCode::NOT_FOUND,
170 }
171 }
172}
173
174impl From<CredsError> for ServiceError {
175 fn from(e: CredsError) -> ServiceError {
176 match e {
177 CredsError::UsernameCaseMappedError => ServiceError::UsernameCaseMappedError,
178 CredsError::ProfainityError => ServiceError::ProfainityError,
179 CredsError::BlacklistError => ServiceError::BlacklistError,
180 CredsError::NotAnEmail => ServiceError::NotAnEmail,
181 CredsError::Argon2Error(_) => ServiceError::InternalServerError,
182 CredsError::PasswordTooLong => ServiceError::PasswordTooLong,
183 CredsError::PasswordTooShort => ServiceError::PasswordTooShort,
184 }
185 }
186}
187
188impl From<DBError> for ServiceError {
189 fn from(e: DBError) -> ServiceError {
190 println!("from conversin: {}", e);
191 match e {
192 DBError::UsernameTaken => ServiceError::UsernameTaken,
193 DBError::SecretTaken => ServiceError::InternalServerError,
194 DBError::EmailTaken => ServiceError::EmailTaken,
195 DBError::AccountNotFound => ServiceError::AccountNotFound,
196 DBError::CaptchaNotFound => ServiceError::CaptchaNotFound,
197 DBError::TrafficPatternNotFound => ServiceError::TrafficPatternNotFound,
198 _ => ServiceError::DBError(DBErrorWrapper(e)),
199 }
200 }
201}
202
203impl From<ValidationErrors> for ServiceError {
204 fn from(_: ValidationErrors) -> ServiceError {
205 ServiceError::NotAnEmail
206 }
207}
208
209impl From<ParseError> for ServiceError {
210 fn from(_: ParseError) -> ServiceError {
211 ServiceError::NotAUrl
212 }
213}
214
215impl From<CaptchaError> for ServiceError {
216 fn from(e: CaptchaError) -> ServiceError {
217 ServiceError::CaptchaError(e)
218 }
219}
220
221impl From<SmtpError> for ServiceError {
222 fn from(e: SmtpError) -> Self {
223 ServiceError::UnableToSendEmail(SmtpErrorWrapper(e))
224 }
225}
226
227impl From<RecvError> for ServiceError {
228 fn from(e: RecvError) -> Self {
229 log::error!("{:?}", e);
230 ServiceError::InternalServerError
231 }
232}
233
234impl From<MailboxError> for ServiceError {
235 fn from(e: MailboxError) -> Self {
236 log::error!("{:?}", e);
237 ServiceError::InternalServerError
238 }
239}
240
241pub type ServiceResult<V> = std::result::Result<V, ServiceError>;
242
243#[derive(Debug, Display, PartialEq, Error)]
244pub enum PageError {
245 #[display(fmt = "Something weng wrong: Internal server error")]
246 InternalServerError,
247
248 #[display(fmt = "{}", _0)]
249 ServiceError(ServiceError),
250}
251
252impl From<ServiceError> for PageError {
253 fn from(e: ServiceError) -> Self {
254 PageError::ServiceError(e)
255 }
256}
257
258impl From<DBError> for PageError {
259 fn from(e: DBError) -> Self {
260 let se: ServiceError = e.into();
261 se.into()
262 }
263}
264
265impl ResponseError for PageError {
266 fn error_response(&self) -> HttpResponse {
267 use crate::PAGES;
268 match self.status_code() {
269 StatusCode::INTERNAL_SERVER_ERROR => HttpResponse::Found()
270 .append_header((header::LOCATION, PAGES.errors.internal_server_error))
271 .finish(),
272 _ => HttpResponse::Found()
273 .append_header((header::LOCATION, PAGES.errors.unknown_error))
274 .finish(),
275 }
276 }
277
278 fn status_code(&self) -> StatusCode {
279 match self {
280 PageError::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR,
281 PageError::ServiceError(e) => e.status_code(),
282 }
283 }
284}
285
286pub type PageResult<V> = std::result::Result<V, PageError>;
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291 use crate::PAGES;
292
293 #[test]
294 fn error_works() {
295 let resp: HttpResponse = PageError::InternalServerError.error_response();
296 assert_eq!(resp.status(), StatusCode::FOUND);
297 let headers = resp.headers();
298 assert_eq!(
299 headers.get(header::LOCATION).unwrap(),
300 PAGES.errors.internal_server_error
301 );
302 }
303}