use std::fmt;
use reqwest::{header::HeaderMap, StatusCode, Url};
use rp_postgrest_error::{DecodeError, PostgrestError};
#[derive(Clone, Debug)]
pub struct ResponseMetadata {
status: StatusCode,
headers: HeaderMap,
url: Url,
}
impl ResponseMetadata {
pub(crate) fn from_response(response: &reqwest::Response) -> Self {
Self {
status: response.status(),
headers: response.headers().clone(),
url: response.url().clone(),
}
}
#[must_use]
pub const fn status(&self) -> StatusCode {
self.status
}
#[must_use]
pub const fn headers(&self) -> &HeaderMap {
&self.headers
}
#[must_use]
pub const fn url(&self) -> &Url {
&self.url
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum ExecuteError {
Request(reqwest::Error),
ResponseBody {
metadata: ResponseMetadata,
source: reqwest::Error,
},
Postgrest {
metadata: ResponseMetadata,
source: PostgrestError,
},
Decode {
metadata: ResponseMetadata,
source: DecodeError,
},
}
impl ExecuteError {
#[must_use]
pub const fn response_metadata(&self) -> Option<&ResponseMetadata> {
match self {
Self::Request(_) => None,
Self::ResponseBody { metadata, .. }
| Self::Postgrest { metadata, .. }
| Self::Decode { metadata, .. } => Some(metadata),
}
}
#[must_use]
pub fn status(&self) -> Option<StatusCode> {
self.response_metadata()
.map(ResponseMetadata::status)
.or_else(|| match self {
Self::Request(source) => source.status(),
_ => None,
})
}
#[must_use]
pub fn url(&self) -> Option<&Url> {
self.response_metadata()
.map(ResponseMetadata::url)
.or_else(|| match self {
Self::Request(source) => source.url(),
_ => None,
})
}
}
impl fmt::Display for ExecuteError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Request(source) => {
write!(formatter, "failed to execute PostgREST request: {source}")
}
Self::ResponseBody { metadata, source } => write!(
formatter,
"failed to read PostgREST response body with status {}: {source}",
metadata.status()
),
Self::Postgrest { source, .. } => source.fmt(formatter),
Self::Decode { source, .. } => source.fmt(formatter),
}
}
}
impl std::error::Error for ExecuteError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Request(source) => Some(source),
Self::ResponseBody { source, .. } => Some(source),
Self::Postgrest { source, .. } => Some(source),
Self::Decode { source, .. } => Some(source),
}
}
}