diff --git a/Cargo.toml b/Cargo.toml index 9a91b30..8c03fec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ deadpool = "0.12" diesel = { version = "2.2", features = ["uuid"] } diesel-async = { version = "0.5", features = ["deadpool", "postgres", "async-connection-wrapper"] } diesel_migrations = { version = "2.2.0", features = ["postgres"] } +thiserror = "2.0.12" [dependencies.tokio] version = "1.44" diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..5d10251 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,79 @@ +use std::{io, time::SystemTimeError}; + +use actix_web::{error::ResponseError, http::{header::{ContentType, ToStrError}, StatusCode}, HttpResponse}; +use deadpool::managed::{BuildError, PoolError}; +use redis::RedisError; +use serde::Serialize; +use thiserror::Error; +use diesel::{result::Error as DieselError, ConnectionError}; +use diesel_async::pooled_connection::PoolError as DieselPoolError; +use tokio::task::JoinError; +use serde_json::Error as JsonError; +use toml::de::Error as TomlError; +use log::error; + +#[derive(Debug, Error)] +pub enum Error { + #[error(transparent)] + SqlError(#[from] DieselError), + #[error(transparent)] + PoolError(#[from] PoolError), + #[error(transparent)] + BuildError(#[from] BuildError), + #[error(transparent)] + RedisError(#[from] RedisError), + #[error(transparent)] + ConnectionError(#[from] ConnectionError), + #[error(transparent)] + JoinError(#[from] JoinError), + #[error(transparent)] + IoError(#[from] io::Error), + #[error(transparent)] + TomlError(#[from] TomlError), + #[error(transparent)] + JsonError(#[from] JsonError), + #[error(transparent)] + SystemTimeError(#[from] SystemTimeError), + #[error(transparent)] + ToStrError(#[from] ToStrError), + #[error(transparent)] + RandomError(#[from] getrandom::Error), + #[error("{0}")] + PasswordHashError(String), + #[error("{0}")] + BadRequest(String), + #[error("{0}")] + Unauthorized(String), +} + +impl ResponseError for Error { + fn error_response(&self) -> HttpResponse { + error!("{}: {}", self.status_code(), self.to_string()); + + HttpResponse::build(self.status_code()) + .insert_header(ContentType::json()) + .json(WebError::new(self.to_string())) + } + + fn status_code(&self) -> StatusCode { + match *self { + Error::SqlError(DieselError::NotFound) => StatusCode::NOT_FOUND, + Error::BadRequest(_) => StatusCode::BAD_REQUEST, + Error::Unauthorized(_) => StatusCode::UNAUTHORIZED, + _ => StatusCode::INTERNAL_SERVER_ERROR, + } + } +} + +#[derive(Serialize)] +struct WebError { + message: String, +} + +impl WebError { + fn new(message: String) -> Self { + Self { + message, + } + } +}