use std::fmt::{self, Display, Formatter};
use arangors_lite::ClientError;
use thiserror::private::AsDynError;
pub use {
arango_error::ArangoError, arango_http_error::ArangoHttpError, database_error::DatabaseError,
};
mod arango_error;
mod arango_http_error;
mod database_error;
#[derive(Debug)]
pub enum Error {
InternalError {
message: Option<String>,
},
ValidationError(String),
NotFound {
item: String,
id: String,
source: Option<DatabaseError>,
},
UnprocessableEntity {
source: Box<dyn std::error::Error>,
},
ArangoError(DatabaseError),
Conflict(DatabaseError),
InitError {
item: String,
message: String,
},
Unauthorized(Option<DatabaseError>),
Forbidden(Option<DatabaseError>),
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Error::InternalError { .. } => "Internal Error".to_string(),
Error::ValidationError(str) => format!("Validations failed: `{}`", str),
Error::NotFound { item, id, .. } => format!("{} {} not found", item, id),
Error::UnprocessableEntity { .. } => "Unprocessable Entity".to_string(),
Error::ArangoError(_) => "ArangoDB Error".to_string(),
Error::Conflict(_) => "Conflict".to_string(),
Error::InitError { item, message, .. } =>
format!("Failed to initialize `{}`: `{}`", item, message),
Error::Unauthorized(_) => "Unauthorized".to_string(),
Error::Forbidden(_) => "Forbidden".to_string(),
}
)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::InternalError { .. } | Error::ValidationError(_) | Error::InitError { .. } => {
None
}
Error::UnprocessableEntity { source } => Some(source.as_ref()),
Error::ArangoError(e) | Error::Conflict(e) => Some(e),
Error::Unauthorized(source)
| Error::Forbidden(source)
| Error::NotFound { source, .. } => source.as_ref().map(AsDynError::as_dyn_error),
}
}
}
impl Error {
#[allow(dead_code)]
#[must_use]
#[inline]
pub const fn http_code(&self) -> u16 {
match self {
Self::ValidationError(_str) => 400,
Self::UnprocessableEntity { .. } => 422,
Self::NotFound { .. } => 404,
Self::Forbidden(_) => 403,
Self::Unauthorized(_) => 401,
Self::ArangoError(_) | Self::InitError { .. } | Self::InternalError { .. } => 500,
Self::Conflict(_) => 409,
}
}
}
impl From<ClientError> for Error {
fn from(error: ClientError) -> Self {
log::debug!("Client Error: {}", error);
match error {
ClientError::Arango(arango_error) => {
let arango_error = DatabaseError::from(arango_error);
match arango_error.http_error {
ArangoHttpError::Unauthorized => Self::Unauthorized(Some(arango_error)),
ArangoHttpError::Forbidden => Self::Forbidden(Some(arango_error)),
ArangoHttpError::Conflict => Self::Conflict(arango_error),
_ => Self::ArangoError(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.to_string(),
},
}
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Self::UnprocessableEntity {
source: Box::new(err),
}
}
}
impl Default for Error {
fn default() -> Self {
Self::InternalError { message: None }
}
}