use crate::{BoxedError, DefaultFuture};
use futures::IntoFuture;
use http::StatusCode;
use std::{borrow::Cow, error, fmt};
#[derive(Debug)]
pub struct Error {
status: StatusCode,
allowed_methods: Cow<'static, [&'static http::Method]>,
source: Option<BoxedError>,
}
impl Error {
fn new(
status: StatusCode,
allowed_methods: Cow<'static, [&'static http::Method]>,
source: Option<BoxedError>,
) -> Self {
assert!(
status.is_client_error() || status.is_server_error(),
"hyperdrive::Error must be created with an error status, not {}",
status,
);
Self {
status,
allowed_methods,
source,
}
}
pub fn from_status(status: StatusCode) -> Self {
Self::new(status, (&[][..]).into(), None)
}
pub fn with_source<S>(status: StatusCode, source: S) -> Self
where
S: Into<BoxedError>,
{
Self::new(status, (&[][..]).into(), Some(source.into()))
}
pub fn wrong_method<M>(allowed_methods: M) -> Self
where
M: Into<Cow<'static, [&'static http::Method]>>,
{
Self::new(StatusCode::METHOD_NOT_ALLOWED, allowed_methods.into(), None)
}
pub fn http_status(&self) -> StatusCode {
self.status
}
pub fn response(&self) -> http::Response<()> {
let mut builder = http::Response::builder();
builder.status(self.http_status());
if self.status == StatusCode::METHOD_NOT_ALLOWED {
let allowed = self
.allowed_methods
.iter()
.map(|method| method.as_str().to_uppercase())
.collect::<Vec<_>>()
.join(", ");
builder.header(http::header::ALLOW, allowed);
}
builder
.body(())
.expect("could not build HTTP response for error")
}
#[doc(hidden)] pub fn into_future<T: Send + 'static>(self) -> DefaultFuture<T, BoxedError> {
Box::new(Err(BoxedError::from(self)).into_future())
}
pub fn allowed_methods(&self) -> Option<&[&'static http::Method]> {
if self.status == StatusCode::METHOD_NOT_ALLOWED {
Some(&self.allowed_methods)
} else {
None
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.source {
None => write!(f, "{}", self.status),
Some(source) => write!(f, "{}: {}", self.status, source),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self.source {
Some(source) => Some(&**source),
None => None,
}
}
}