use std::fmt;
use http::StatusCode;
use hyper::ext::ReasonPhrase;
use thiserror::Error;
#[derive(Debug, Error)]
#[error(transparent)]
pub enum Error {
UrlParseError(#[from] url::ParseError),
#[error("Builder error: {0}")]
BuilderError(#[from] BuilderError),
HttpError(#[from] http::Error),
HyperError(#[from] hyper::Error),
#[cfg(feature = "json")]
#[error("Error decoding response body.")]
Decode(#[from] serde_json::Error),
InvalidUriParts(#[from] http::uri::InvalidUriParts),
ClientError(#[from] hyper_util::client::legacy::Error),
StatusError(#[from] StatusError),
}
impl Error {
pub fn is_builder(&self) -> bool {
matches!(self, Self::BuilderError(..))
}
pub fn is_status(&self) -> bool {
matches!(self, Self::StatusError { .. })
}
pub fn is_connect(&self) -> bool {
matches!(self, Self::ClientError(err) if err.is_connect())
}
pub fn status(&self) -> Option<StatusCode> {
match self {
Self::StatusError(err) => Some(err.code),
_ => None,
}
}
}
#[derive(Debug, Error)]
#[error(transparent)]
pub enum BuilderError {
UrlParse(#[from] url::ParseError),
Http(#[from] http::Error),
SerializeUrl(#[from] serde_urlencoded::ser::Error),
#[cfg(feature = "json")]
SerializeJson(#[from] serde_json::Error),
}
#[derive(Debug)]
pub struct StatusError {
code: StatusCode,
reason: Option<ReasonPhrase>,
}
impl StatusError {
pub(crate) fn new(code: StatusCode, reason: Option<ReasonPhrase>) -> Self {
Self { code, reason }
}
}
impl fmt::Display for StatusError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let code = self.code;
let prefix = if self.code.is_client_error() {
"HTTP status client error"
} else {
"HTTP status server error"
};
match self
.reason
.as_ref()
.and_then(|r| std::str::from_utf8(r.as_bytes()).ok())
{
Some(reason) => {
write!(f, "{prefix} ({code} {reason})")
}
None => write!(f, "{prefix} ({code})"),
}
}
}
impl std::error::Error for StatusError {}
pub type Result<T> = std::result::Result<T, Error>;