use {
crate::{output::ResponseBody, util::Never},
http::{Request, Response, StatusCode},
std::{any::Any, fmt, io},
};
pub type Result<T> = std::result::Result<T, Error>;
pub trait HttpError: fmt::Display + fmt::Debug + Send + 'static + Sized {
type Body: Into<ResponseBody>;
fn into_response(self, request: &Request<()>) -> Response<Self::Body>;
}
impl HttpError for StatusCode {
type Body = ();
fn into_response(self, _: &Request<()>) -> Response<Self::Body> {
let mut response = Response::new(());
*response.status_mut() = self;
response
}
}
impl HttpError for io::Error {
type Body = String;
fn into_response(self, _: &Request<()>) -> Response<Self::Body> {
Response::builder()
.status(match self.kind() {
io::ErrorKind::NotFound => StatusCode::NOT_FOUND,
io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN,
_ => StatusCode::INTERNAL_SERVER_ERROR,
})
.body(format!("I/O error: {}", self))
.expect("should be a valid response")
}
}
impl HttpError for failure::Error {
type Body = String;
fn into_response(self, _: &Request<()>) -> Response<Self::Body> {
Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(format!("generic error: {}", self))
.expect("should be a valid response")
}
}
impl HttpError for hyper::Error {
type Body = String;
fn into_response(self, _: &Request<()>) -> Response<Self::Body> {
Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(format!("hyper error: {}", self))
.expect("should be a valid response")
}
}
impl HttpError for Never {
type Body = ResponseBody;
fn into_response(self, _: &Request<()>) -> Response<Self::Body> {
match self {}
}
}
#[derive(Debug)]
pub struct ErrorResponse<T> {
inner: Response<T>,
}
#[allow(missing_docs)]
impl<T> ErrorResponse<T>
where
T: fmt::Debug + fmt::Display + Send + 'static,
{
pub fn new(inner: Response<T>) -> Self {
debug_assert!(inner.status().is_client_error() || inner.status().is_server_error());
Self { inner }
}
pub fn into_inner(self) -> Response<T> {
self.inner
}
}
impl<D> From<Response<D>> for ErrorResponse<D>
where
D: fmt::Debug + fmt::Display + Send + 'static,
{
fn from(response: Response<D>) -> Self {
Self::new(response)
}
}
impl<D> fmt::Display for ErrorResponse<D>
where
D: fmt::Debug + fmt::Display + Send + 'static,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.inner.body(), f)
}
}
impl<D> HttpError for ErrorResponse<D>
where
D: fmt::Debug + fmt::Display + Send + 'static,
{
type Body = String;
fn into_response(self, _: &Request<()>) -> Response<Self::Body> {
self.inner.map(|body| body.to_string())
}
}
#[allow(missing_docs)]
pub fn error_response<D>(response: Response<D>) -> Error
where
D: fmt::Debug + fmt::Display + Send + 'static,
{
ErrorResponse::new(response).into()
}
#[allow(missing_docs)]
pub fn custom<D>(status: StatusCode, msg: D) -> Error
where
D: fmt::Debug + fmt::Display + Send + 'static,
{
let mut response = Response::new(msg);
*response.status_mut() = status;
error_response(response)
}
macro_rules! define_errors {
($(
$(#[$m:meta])*
$name:ident => $STATUS:ident,
)*) => {$(
$(#[$m])*
#[inline]
pub fn $name<D>(msg: D) -> Error
where
D: fmt::Debug + fmt::Display + Send + 'static,
{
self::custom(StatusCode::$STATUS, msg)
}
)*};
}
define_errors! {
bad_request => BAD_REQUEST,
unauthorized => UNAUTHORIZED,
forbidden => FORBIDDEN,
not_found => NOT_FOUND,
method_not_allowed => METHOD_NOT_ALLOWED,
internal_server_error => INTERNAL_SERVER_ERROR,
}
type AnyObj = dyn Any + Send + 'static;
pub struct Error {
obj: Box<AnyObj>,
fmt_debug_fn: fn(&AnyObj, &mut fmt::Formatter<'_>) -> fmt::Result,
fmt_display_fn: fn(&AnyObj, &mut fmt::Formatter<'_>) -> fmt::Result,
into_response_fn: fn(Box<AnyObj>, &Request<()>) -> Response<ResponseBody>,
}
impl fmt::Debug for Error {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
(self.fmt_debug_fn)(&self.obj, formatter)
}
}
impl fmt::Display for Error {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
(self.fmt_display_fn)(&self.obj, formatter)
}
}
impl<E> From<E> for Error
where
E: HttpError,
{
fn from(err: E) -> Self {
Self::new(err)
}
}
impl Error {
pub fn new<E: HttpError>(err: E) -> Self {
fn fmt_debug<E: HttpError>(this: &AnyObj, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let this = this.downcast_ref::<E>().expect("the wrong type id");
fmt::Debug::fmt(this, f)
}
fn fmt_display<E: HttpError>(this: &AnyObj, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let this = this.downcast_ref::<E>().expect("the wrong type id");
fmt::Display::fmt(this, f)
}
fn into_response<E: HttpError>(
this: Box<AnyObj>,
request: &Request<()>,
) -> Response<ResponseBody> {
let this = *this.downcast::<E>().expect("the wrong type id");
HttpError::into_response(this, request).map(Into::into)
}
Error {
obj: Box::new(err),
fmt_debug_fn: fmt_debug::<E>,
fmt_display_fn: fmt_display::<E>,
into_response_fn: into_response::<E>,
}
}
#[inline]
pub fn is<T: HttpError>(&self) -> bool {
self.obj.is::<T>()
}
#[inline]
pub fn downcast_ref<T: HttpError>(&self) -> Option<&T> {
self.obj.downcast_ref()
}
#[inline]
pub fn downcast_mut<T: HttpError>(&mut self) -> Option<&mut T> {
self.obj.downcast_mut()
}
#[inline]
pub fn downcast<T: HttpError>(self) -> std::result::Result<T, Self> {
if self.obj.is::<T>() {
Ok(*self.obj.downcast().expect("never fails"))
} else {
Err(self)
}
}
pub fn into_response(self, request: &Request<()>) -> Response<ResponseBody> {
(self.into_response_fn)(self.obj, request)
}
}