use std::fmt::Debug;
use thiserror::Error;
#[derive(Error, Clone, Debug, PartialEq, Eq)]
pub enum ServiceError {
#[error("Url parsing error.")]
BadUrl(String),
#[error("Token parsing error.")]
BadToken,
#[error("Headers parsing error.")]
BadHeader(String),
#[error("Set an URL before calling build.")]
UnitializedUrl,
#[error("Set a token before calling build.")]
UnitializedToken,
#[error("http error: {0}")]
Request(RequestError),
#[error("Msg: {0}")]
Msg(String),
#[error("Internal error: {0}")]
Internal(String),
}
impl ServiceError {
pub fn is_404(&self) -> bool {
if let Some(e) = self.as_request_error() {
e.is_404()
} else {
false
}
}
fn as_request_error(&self) -> Option<&RequestError> {
match self {
Self::Request(e) => Some(e),
_ => None,
}
}
}
#[derive(Error, Clone, Debug, PartialEq, Eq)]
#[error("{:?}, {}", url, details)]
pub struct RequestError {
pub url: Option<String>,
pub details: RequestErrorDetails,
}
impl RequestError {
pub fn new(url: Option<String>, details: RequestErrorDetails) -> RequestError {
RequestError { url, details }
}
fn is_404(&self) -> bool {
self.details.status() == Some(404)
}
}
#[derive(Error, Clone, Debug, PartialEq, Eq)]
pub enum RequestErrorDetails {
#[error("Http error: {}, {}", status, message)]
Http { status: u16, message: String },
#[error("Timeout connecting to the server.")]
Timeout,
#[error("Client error: {}", description)]
Client { description: String },
}
impl RequestErrorDetails {
fn status(&self) -> Option<u16> {
match self {
Self::Http { status, .. } => Some(*status),
_ => None,
}
}
}
impl From<algonaut_client::error::ClientError> for ServiceError {
fn from(error: algonaut_client::error::ClientError) -> Self {
match error {
algonaut_client::error::ClientError::BadUrl(msg) => ServiceError::BadUrl(msg),
algonaut_client::error::ClientError::BadToken => ServiceError::BadToken,
algonaut_client::error::ClientError::BadHeader(msg) => ServiceError::BadHeader(msg),
algonaut_client::error::ClientError::Request(e) => ServiceError::Request(e.into()),
algonaut_client::error::ClientError::Msg(msg) => ServiceError::Msg(msg),
}
}
}
impl From<algonaut_client::error::RequestError> for RequestError {
fn from(error: algonaut_client::error::RequestError) -> Self {
RequestError::new(error.url.clone(), error.details.into())
}
}
impl From<algonaut_client::error::RequestErrorDetails> for RequestErrorDetails {
fn from(details: algonaut_client::error::RequestErrorDetails) -> Self {
match details {
algonaut_client::error::RequestErrorDetails::Http { status, message } => {
RequestErrorDetails::Http { status, message }
}
algonaut_client::error::RequestErrorDetails::Timeout => RequestErrorDetails::Timeout {},
algonaut_client::error::RequestErrorDetails::Client { description } => {
RequestErrorDetails::Client { description }
}
}
}
}
impl From<rmp_serde::encode::Error> for ServiceError {
fn from(error: rmp_serde::encode::Error) -> Self {
ServiceError::Internal(error.to_string())
}
}
impl From<String> for ServiceError {
fn from(error: String) -> Self {
ServiceError::Internal(error)
}
}
#[test]
fn check_404() {
let not_found_error = ServiceError::Request(RequestError::new(
Some("testing".to_owned()),
RequestErrorDetails::Http {
status: 404,
message: "not found".to_owned(),
},
));
let bad_request_error = ServiceError::Request(RequestError::new(
None,
RequestErrorDetails::Http {
status: 400,
message: "bad request".to_owned(),
},
));
let unrelated_error = ServiceError::UnitializedToken;
assert!(
not_found_error.is_404(),
"a 404 request error is saying that it is not a 404 error"
);
assert!(
!bad_request_error.is_404(),
"a 400 request error is saying that it is a 404 error"
);
assert!(
!unrelated_error.is_404(),
"an unrelated request error is saying that it is a 404 error"
);
}