use reqwest::header::InvalidHeaderValue;
use serde::Serialize;
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::sync::PoisonError;
pub type DshApiResult<T> = Result<T, DshApiError>;
#[derive(Clone, Debug, Serialize)]
pub enum DshApiError {
BadRequest { message: Option<String> },
Configuration { message: String },
Conversion { message: String },
NotAuthorized { message: Option<String> },
NotFound { message: Option<String> },
Parameter { message: String },
Unexpected { message: String, cause: Option<String> },
Unprocessable { message: Option<String> },
}
impl DshApiError {
pub(crate) fn configuration<T>(message: T) -> Self
where
T: Into<String>,
{
Self::Configuration { message: message.into() }
}
pub(crate) fn conversion<T>(message: T) -> Self
where
T: Into<String>,
{
Self::Conversion { message: message.into() }
}
pub(crate) fn not_found() -> Self {
Self::NotFound { message: None }
}
#[allow(dead_code)]
pub(crate) fn parameter<T>(message: T) -> Self
where
T: Into<String>,
{
Self::Conversion { message: message.into() }
}
pub(crate) fn unexpected<T>(message: T) -> Self
where
T: Into<String>,
{
Self::Unexpected { message: message.into(), cause: None }
}
}
#[macro_export]
macro_rules! log_error {
() => {{
|error| {
log::error!("{}", error);
log::debug!("{:?}", error);
error
}
}};
($($t:tt)*) => {{
|error| {
log::error!("{}{}", format!($($t)*), error);
log::debug!("{:?}", error);
error
}
}};
}
impl Error for DshApiError {}
impl Display for DshApiError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
DshApiError::BadRequest { message } => match message {
Some(message) => write!(f, "bad request: {}", message),
None => write!(f, "bad request"),
},
DshApiError::Configuration { message } => write!(f, "configuration error: {}", message),
DshApiError::Conversion { message } => write!(f, "conversion error: {}", message),
DshApiError::NotAuthorized { message } => match message {
Some(message) => write!(f, "not authorized: {}", message),
None => write!(f, "not authorized"),
},
DshApiError::NotFound { message } => match message {
Some(message) => write!(f, "not found: {}", message),
None => write!(f, "not found"),
},
DshApiError::Parameter { message } => write!(f, "parameter error: {}", message),
DshApiError::Unexpected { message, cause } => match cause {
Some(cause) => write!(f, "unexpected error: {} ({})", message, cause),
None => write!(f, "unexpected error: {}", message),
},
DshApiError::Unprocessable { message } => match message {
Some(message) => write!(f, "unprocessable entity: {}", message),
None => write!(f, "unprocessable entity"),
},
}
}
}
impl From<crate::types::error::ConversionError> for DshApiError {
fn from(conversion_error: crate::types::error::ConversionError) -> Self {
DshApiError::unexpected(conversion_error.to_string())
}
}
impl From<serde_json::Error> for DshApiError {
fn from(json_error: serde_json::Error) -> Self {
DshApiError::conversion(json_error.to_string())
}
}
impl From<reqwest::Error> for DshApiError {
fn from(reqwest_error: reqwest::Error) -> Self {
DshApiError::unexpected(reqwest_error.to_string())
}
}
impl From<InvalidHeaderValue> for DshApiError {
fn from(invalid_header_error: InvalidHeaderValue) -> Self {
DshApiError::unexpected(invalid_header_error.to_string())
}
}
impl<T> From<PoisonError<T>> for DshApiError {
fn from(poison_value: PoisonError<T>) -> Self {
DshApiError::Unexpected { message: "mutex error".to_string(), cause: Some(poison_value.to_string()) }
}
}
impl From<std::str::Utf8Error> for DshApiError {
fn from(utf8_error: std::str::Utf8Error) -> Self {
DshApiError::unexpected(utf8_error.to_string())
}
}
impl From<String> for DshApiError {
fn from(message: String) -> Self {
DshApiError::unexpected(message)
}
}
impl From<&str> for DshApiError {
fn from(message: &str) -> Self {
DshApiError::unexpected(message)
}
}
impl From<std::time::SystemTimeError> for DshApiError {
fn from(system_time_error: std::time::SystemTimeError) -> Self {
DshApiError::Unexpected { message: "system time error".to_string(), cause: Some(system_time_error.to_string()) }
}
}
impl From<DshApiError> for String {
fn from(value: DshApiError) -> Self {
value.to_string()
}
}
#[test]
fn test_dsh_api_error_is_send() {
fn assert_send<T: Send>() {}
assert_send::<DshApiError>();
}
#[test]
fn test_dsh_api_error_is_sync() {
fn assert_sync<T: Sync>() {}
assert_sync::<DshApiError>();
}