use std::error::Error;
#[cfg(feature = "actix")]
use actix_web::{error, http::StatusCode};
use arangors::ClientError;
#[cfg(feature = "open-api")]
use paperclip::actix::api_v2_errors;
use thiserror::Error as ErrorDerive;
pub use {
arango_error::ArangoError, arango_http_error::ArangoHttpError, database_error::DatabaseError,
};
mod arango_error;
mod arango_http_error;
mod database_error;
#[cfg_attr(feature = "open-api", api_v2_errors())]
#[derive(ErrorDerive, Debug)]
pub enum ServiceError {
#[error("Internal error")]
InternalError {
message: Option<String>,
},
#[error("Validations failed: `{0}`")]
ValidationError(String),
#[error("{item} {id} not found")]
NotFound {
item: String,
id: String,
#[source]
source: Option<DatabaseError>,
},
#[error("Unprocessable Entity")]
UnprocessableEntity {
#[source]
source: Box<dyn Error>,
},
#[error("Internal Error")]
ArangoError(#[source] DatabaseError),
#[error("Failed to initialize `{item}`: `{message}`")]
InitError {
item: String,
message: String,
},
#[error("Unauthorized")]
Unauthorized,
#[error("Forbidden")]
Forbidden,
}
#[cfg(feature = "actix")]
impl error::ResponseError for ServiceError {
fn status_code(&self) -> StatusCode {
match self {
Self::ValidationError(_str) => StatusCode::BAD_REQUEST,
Self::UnprocessableEntity { .. } => StatusCode::UNPROCESSABLE_ENTITY,
Self::NotFound { .. } => StatusCode::NOT_FOUND,
Self::Forbidden => StatusCode::FORBIDDEN,
Self::Unauthorized => StatusCode::UNAUTHORIZED,
Self::ArangoError(db_error) => match db_error.http_error {
ArangoHttpError::Conflict => StatusCode::CONFLICT,
_ => StatusCode::INTERNAL_SERVER_ERROR,
},
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
impl ServiceError {
#[allow(dead_code)]
pub fn http_code(&self) -> &str {
match self {
Self::ValidationError(_str) => "400",
Self::UnprocessableEntity { .. } => "422",
Self::NotFound { .. } => "404",
Self::Forbidden => "403",
Self::Unauthorized => "401",
Self::ArangoError(db_error) => match db_error.http_error {
ArangoHttpError::Conflict => "409",
_ => "500",
},
_ => "500",
}
}
}
impl From<ClientError> for ServiceError {
fn from(error: ClientError) -> Self {
log::debug!("Client Error: {}", error);
match error {
ClientError::Arango(arango_error) => {
Self::ArangoError(DatabaseError::from(arango_error))
}
ClientError::Serde(serde_error) => Self::UnprocessableEntity {
source: Box::new(serde_error),
},
ClientError::InvalidServer(server) => Self::InitError {
item: server,
message: String::from("Is not ArangoDB"),
},
ClientError::InsufficientPermission {
permission,
operation,
} => Self::InitError {
item: operation.clone(),
message: format!(
"Insufficent permission for {} : {:?}",
operation, permission
),
},
ClientError::HttpClient(error) => Self::InitError {
item: "Http Client".to_string(),
message: error,
},
}
}
}
impl Default for ServiceError {
fn default() -> Self {
Self::InternalError { message: None }
}
}