use std::{any, error::Error};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum BodyError {}
#[derive(Debug, Error)]
pub enum ApiError<E>
where
E: Error + Send + Sync + 'static,
{
#[error("client error: {}", source)]
Client { source: E },
#[error("failed to create form data: {}", source)]
Body {
#[from]
source: BodyError,
},
#[error("failed to parse JSON response: {}", source)]
Json {
#[from]
source: serde_json::Error,
},
#[error("server responded with error: {}", msg)]
Server { msg: String },
#[error("internal server error: {}", status)]
Internal {
status: http::StatusCode,
data: Vec<u8>,
},
#[error("server responded with error object: {:?}", obj)]
ServerObject { obj: serde_json::Value },
#[error("server responded with unknown object: {:?}", obj)]
UnknownObject { obj: serde_json::Value },
#[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(crate) fn internal_error(status: http::StatusCode, body: &bytes::Bytes) -> Self {
Self::Internal {
status,
data: body.into_iter().copied().collect(),
}
}
pub(crate) fn server_error(value: serde_json::Value) -> Self {
let error_value = value
.pointer("/message")
.or_else(|| value.pointer("/error"));
if let Some(error_value) = error_value {
if let Some(msg) = error_value.as_str() {
ApiError::Server { msg: msg.into() }
} else {
ApiError::ServerObject {
obj: error_value.clone(),
}
}
} else {
ApiError::UnknownObject { obj: value }
}
}
pub(crate) fn data_type<T>(source: serde_json::Error) -> Self {
ApiError::DataType {
source,
typename: any::type_name::<T>(),
}
}
}