From 26a4d1f39f06fecbb6f1b95d125c2d8dc117ed48 Mon Sep 17 00:00:00 2001 From: realaravinth Date: Wed, 18 May 2022 20:26:58 +0530 Subject: [PATCH] feat: bootstrap error handling --- src/errors.rs | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/errors.rs diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..4f8f351 --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,156 @@ +/* + * ForgeFlux StarChart - A federated software forge spider + * Copyright (C) 2022 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +use std::convert::From; + +use actix_web::{ + error::ResponseError, + http::{header, StatusCode}, + HttpResponse, HttpResponseBuilder, +}; +use db_core::errors::DBError; +use derive_more::{Display, Error}; +use serde::{Deserialize, Serialize}; +use url::ParseError; +use validator::ValidationErrors; + +#[derive(Debug, Display, Error)] +pub struct DBErrorWrapper(DBError); + +impl std::cmp::PartialEq for DBErrorWrapper { + fn eq(&self, other: &Self) -> bool { + format!("{}", self.0) == format!("{}", other.0) + } +} + +#[derive(Debug, Display, PartialEq, Error)] +#[cfg(not(tarpaulin_include))] +pub enum ServiceError { + #[display(fmt = "internal server error")] + InternalServerError, + + #[display( + fmt = "This server is is closed for registration. Contact admin if this is unexpecter" + )] + ClosedForRegistration, + + #[display(fmt = "The value you entered for email is not an email")] //405j + NotAnEmail, + #[display(fmt = "The value you entered for URL is not a URL")] //405j + NotAUrl, + + #[display(fmt = "{}", _0)] + DBError(DBErrorWrapper), + + /// DNS challenge value is already taken + #[display(fmt = "DNS challenge is already taken")] + DuplicateChallengeText, + + /// DNS challenge hostname is already taken + #[display(fmt = "DNS challenge hostname is already taken")] + DuplicateChallengeHostname, + + /// Hostname is already taken + #[display(fmt = "Hostname is already taken")] + DuplicateHostname, + + /// Forge Type is already taken + #[display(fmt = "Forge Type is already taken")] + DuplicateForgeType, + + /// HTML link Type is already taken + #[display(fmt = "User HTML link is already taken")] + DuplicateUserLink, + + /// Topic is already taken + #[display(fmt = "Topic is already taken")] + DuplicateTopic, + + /// Repository link is already taken + #[display(fmt = "Repository link is already taken")] + DuplicateRepositoryLink, +} + +#[derive(Serialize, Deserialize)] +#[cfg(not(tarpaulin_include))] +pub struct ErrorToResponse { + pub error: String, +} + +#[cfg(not(tarpaulin_include))] +impl ResponseError for ServiceError { + #[cfg(not(tarpaulin_include))] + fn error_response(&self) -> HttpResponse { + HttpResponseBuilder::new(self.status_code()) + .append_header((header::CONTENT_TYPE, "application/json; charset=UTF-8")) + .body( + serde_json::to_string(&ErrorToResponse { + error: self.to_string(), + }) + .unwrap(), + ) + } + + #[cfg(not(tarpaulin_include))] + fn status_code(&self) -> StatusCode { + match self { + ServiceError::ClosedForRegistration => StatusCode::FORBIDDEN, + ServiceError::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR, + ServiceError::NotAnEmail => StatusCode::BAD_REQUEST, + ServiceError::NotAUrl => StatusCode::BAD_REQUEST, + ServiceError::DBError(_) => StatusCode::INTERNAL_SERVER_ERROR, + ServiceError::DuplicateChallengeHostname + | ServiceError::DuplicateHostname + | ServiceError::DuplicateUserLink + | ServiceError::DuplicateTopic + | ServiceError::DuplicateRepositoryLink => StatusCode::BAD_REQUEST, + + ServiceError::DuplicateChallengeText | ServiceError::DuplicateForgeType => { + StatusCode::INTERNAL_SERVER_ERROR + } + } + } +} + +impl From for ServiceError { + #[cfg(not(tarpaulin_include))] + fn from(e: DBError) -> ServiceError { + println!("from conversin: {}", e); + match e { + // TODO: resolve all errors to ServiceError::* + _ => ServiceError::DBError(DBErrorWrapper(e)), + } + } +} + +impl From for ServiceError { + #[cfg(not(tarpaulin_include))] + fn from(_: ValidationErrors) -> ServiceError { + ServiceError::NotAnEmail + } +} + +impl From for ServiceError { + #[cfg(not(tarpaulin_include))] + fn from(_: ParseError) -> ServiceError { + ServiceError::NotAUrl + } +} + +#[cfg(not(tarpaulin_include))] +pub type ServiceResult = std::result::Result;