use crate::utils::{is_client_error, is_server_error};
use soup::{Message, Status, glib};
use std::{error, fmt, io};
#[derive(Debug)]
pub struct Error {
inner: Inner,
uri: Option<glib::Uri>,
}
pub type Result<T> = std::result::Result<T, Error>;
impl Error {
pub fn url(&self) -> Option<&glib::Uri> {
self.uri.as_ref()
}
pub fn without_url(self) -> Self {
Self {
inner: self.inner,
uri: None,
}
}
pub fn status(&self) -> Option<Status> {
if let Inner::Status(status, ..) = self.inner {
Some(status)
} else {
None
}
}
pub fn status_code(&self) -> Option<u32> {
if let Inner::Status(_, status_code, ..) = self.inner {
Some(status_code)
} else {
None
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(uri) = &self.uri {
write!(f, "Request to {uri} failed: {}", self.inner)
} else {
self.inner.fmt(f)
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self.inner {
Inner::Glib(error) => Some(error),
Inner::Io(error) => Some(error),
Inner::Status(..) => None,
#[cfg(feature = "json")]
Inner::Json(error) => Some(error),
#[cfg(any(feature = "form", feature = "query"))]
Inner::Form(error) => Some(error),
}
}
}
impl Error {
pub(crate) fn new(inner: impl Into<Inner>) -> Self {
Self::new_with_uri(inner, None)
}
pub(crate) fn new_with_uri(inner: impl Into<Inner>, uri: Option<glib::Uri>) -> Self {
Self {
inner: inner.into(),
uri,
}
}
pub(crate) fn new_status(message: &Message) -> Self {
Self::new_with_uri(
Inner::Status(
message.status(),
message.status_code(),
message.reason_phrase(),
),
message.uri(),
)
}
}
#[derive(Debug)]
pub(crate) enum Inner {
Glib(glib::Error),
Io(io::Error),
Status(Status, u32, Option<glib::GString>),
#[cfg(feature = "json")]
Json(json::Error),
#[cfg(any(feature = "form", feature = "query"))]
Form(serde_urlencoded::ser::Error),
}
impl fmt::Display for Inner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Glib(error) => error.fmt(f),
Self::Io(error) => error.fmt(f),
Self::Status(_, code, reason) => {
let prefix = if is_client_error(*code) {
"HTTP status client error"
} else {
debug_assert!(is_server_error(*code));
"HTTP status server error"
};
if let Some(reason) = reason {
write!(f, "{prefix} ({code} {reason})")
} else {
write!(f, "{prefix} ({code})")
}
}
#[cfg(feature = "json")]
Self::Json(error) => error.fmt(f),
#[cfg(any(feature = "form", feature = "query"))]
Self::Form(error) => error.fmt(f),
}
}
}
impl From<glib::Error> for Inner {
fn from(value: glib::Error) -> Self {
Inner::Glib(value)
}
}
impl From<io::Error> for Inner {
fn from(value: io::Error) -> Self {
Inner::Io(value)
}
}
#[cfg(feature = "json")]
impl From<json::Error> for Inner {
fn from(value: json::Error) -> Self {
Inner::Json(value)
}
}
#[cfg(any(feature = "form", feature = "query"))]
impl From<serde_urlencoded::ser::Error> for Inner {
fn from(value: serde_urlencoded::ser::Error) -> Self {
Inner::Form(value)
}
}