use std::fmt;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("the server at {base} could not be reached: {source}")]
Transport {
base: String,
#[source]
source: reqwest::Error,
},
#[error("the server refused the request: {0}")]
Refused(String),
#[error("the server reported success with no payload")]
EmptyPayload,
#[error("could not read the server's answer to {route}: {source}")]
Shape {
route: &'static str,
#[source]
source: reqwest::Error,
},
#[error("invalid server address: {0}")]
Address(#[from] url::ParseError),
#[cfg(feature = "blocking")]
#[error("could not start a runtime for the blocking client: {0}")]
Runtime(#[source] std::io::Error),
#[error("the api key cannot be sent as a header: {0}")]
ApiKey(#[from] reqwest::header::InvalidHeaderValue),
}
pub type Result<T> = std::result::Result<T, Error>;
impl Error {
pub fn is_transport(&self) -> bool {
matches!(self, Error::Transport { .. })
}
}
#[derive(serde::Deserialize)]
#[serde(bound(deserialize = "T: serde::Deserialize<'de>"))]
pub(crate) struct Envelope<T> {
success: bool,
#[serde(default)]
data: Option<T>,
#[serde(default)]
error: Option<String>,
}
impl<T> Envelope<T> {
pub(crate) fn into_data(self) -> Result<T> {
match (self.success, self.data, self.error) {
(true, Some(data), _) => Ok(data),
(_, _, Some(reason)) => Err(Error::Refused(reason)),
_ => Err(Error::EmptyPayload),
}
}
}
pub(crate) struct Redacted;
impl fmt::Debug for Redacted {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("<redacted>")
}
}