use std::any;
use std::error::Error;
use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum BodyError {
#[error("failed to create JSON body: {}", source)]
Json {
#[from]
source: serde_json::Error,
},
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ApiError<E>
where
E: Error + Send + Sync + 'static,
{
#[error("client error: {}", source)]
Client {
source: E,
},
#[error("failed to parse url: {}", source)]
UrlParse {
#[from]
source: url::ParseError,
},
#[error("failed to create form data: {}", source)]
Body {
#[from]
source: BodyError,
},
#[error("could not parse JSON response: {}", source)]
Json {
#[from]
source: serde_json::Error,
},
#[error("server responded with error: {}", msg)]
Server {
msg: String,
},
#[error("server responded with error: {} - {}", .status, String::from_utf8_lossy(.data))]
ServerService {
status: http::StatusCode,
data: Vec<u8>,
},
#[error("could not parse {} data from JSON: {}", typename, source)]
DataType {
source: serde_json::Error,
typename: &'static str,
},
}
impl<E> ApiError<E>
where
E: Error + Send + Sync + 'static,
{
pub fn client(source: E) -> Self {
ApiError::Client { source }
}
pub fn map_client<F, W>(self, f: F) -> ApiError<W>
where
F: FnOnce(E) -> W,
W: Error + Send + Sync + 'static,
{
match self {
Self::Client { source } => ApiError::client(f(source)),
Self::UrlParse { source } => ApiError::UrlParse { source },
Self::Body { source } => ApiError::Body { source },
Self::Json { source } => ApiError::Json { source },
Self::Server { msg } => ApiError::Server { msg },
Self::ServerService { status, data } => ApiError::ServerService { status, data },
Self::DataType { source, typename } => ApiError::DataType { source, typename },
}
}
pub(crate) fn server_error(status: http::StatusCode, body: &bytes::Bytes) -> Self {
Self::ServerService {
status,
data: body.into_iter().copied().collect(),
}
}
pub(crate) fn data_type<T>(source: serde_json::Error) -> Self {
ApiError::DataType {
source,
typename: any::type_name::<T>(),
}
}
}