use serde_json::Value as JsonValue;
pub type Result<T> = std::result::Result<T, ClientError>;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ClientError {
#[error("HTTP {status}: {message}")]
Http {
status: u16,
message: String,
body: String,
payload: Option<JsonValue>,
},
#[error("transport error: {}", transport_detail(.0))]
Transport(#[from] reqwest::Error),
#[error("decode error: {0}")]
Decode(String),
#[error("unexpected content type: {0}")]
UnexpectedContentType(String),
#[cfg(feature = "arrow")]
#[error("arrow decode error: {0}")]
Arrow(#[from] arrow::error::ArrowError),
#[error("invalid base url: {0}")]
InvalidBaseUrl(String),
}
fn transport_detail(err: &reqwest::Error) -> String {
use std::error::Error;
let mut msg = err.to_string();
let mut source = err.source();
while let Some(cause) = source {
let text = cause.to_string();
if !text.is_empty() && !msg.contains(&text) {
msg.push_str(": ");
msg.push_str(&text);
}
source = cause.source();
}
msg
}
impl ClientError {
pub(crate) fn from_response(status: u16, body: String) -> Self {
let payload = serde_json::from_str::<JsonValue>(&body).ok();
let message = payload
.as_ref()
.and_then(|v| v.get("error"))
.and_then(|v| v.as_str())
.map(str::to_owned)
.unwrap_or_else(|| body.chars().take(200).collect());
ClientError::Http {
status,
message,
body,
payload,
}
}
}