use std::any::TypeId;
use std::error;
use std::fmt;
use std::fmt::{Debug, Display};
use std::io;
use failure;
use failure::Fail;
use futures::Poll;
use http::header::{HeaderMap, HeaderValue};
use http::{header, Response, StatusCode};
use hyper::body::Payload;
use serde::ser::{Serialize, SerializeMap, Serializer};
pub trait HttpError: Debug + Display + Send + Sync + 'static {
fn status_code(&self) -> StatusCode {
StatusCode::INTERNAL_SERVER_ERROR
}
#[allow(unused_variables)]
fn headers(&self, headers: &mut HeaderMap) {}
#[allow(missing_docs)]
fn cause(&self) -> Option<&dyn Fail> {
None
}
#[doc(hidden)]
fn __private_type_id__(&self) -> TypeId {
TypeId::of::<Self>()
}
}
impl HttpError for io::Error {
fn status_code(&self) -> StatusCode {
match self.kind() {
io::ErrorKind::NotFound => StatusCode::NOT_FOUND,
io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN,
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
impl HttpError for failure::Error {
fn cause(&self) -> Option<&dyn Fail> {
self.as_fail().cause()
}
}
#[derive(Debug)]
pub struct Error(Box<dyn HttpError>);
impl<E: HttpError> From<E> for Error {
fn from(err: E) -> Self {
Error(Box::new(err))
}
}
impl AsRef<dyn HttpError> for Error {
fn as_ref(&self) -> &dyn HttpError {
&*self.0
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&*self.0, f)
}
}
impl Error {
pub fn is<T: HttpError>(&self) -> bool {
self.0.__private_type_id__() == TypeId::of::<T>()
}
pub fn downcast_ref<T: HttpError>(&self) -> Option<&T> {
if self.is::<T>() {
unsafe { Some(&*(&*self.0 as *const dyn HttpError as *const T)) }
} else {
None
}
}
pub fn downcast_mut<T: HttpError>(&mut self) -> Option<&mut T> {
if self.is::<T>() {
unsafe { Some(&mut *(&mut *self.0 as *mut dyn HttpError as *mut T)) }
} else {
None
}
}
pub fn downcast<T: HttpError>(self) -> Result<T> {
if self.is::<T>() {
unsafe {
Ok(*Box::from_raw(
Box::into_raw(self.0) as *mut dyn HttpError as *mut T
))
}
} else {
Err(self)
}
}
pub fn status_code(&self) -> StatusCode {
self.0.status_code()
}
pub fn headers(&self, headers: &mut HeaderMap) {
self.0.headers(headers)
}
pub fn cause(&self) -> Option<&dyn Fail> {
self.0.cause()
}
pub(crate) fn into_response(self) -> Response<Error> {
let mut response = Response::new(());
*response.status_mut() = self.status_code();
response.headers_mut().insert(
header::CONTENT_TYPE,
HeaderValue::from_static("text/plain; charset=utf-8"),
);
self.headers(response.headers_mut());
response.map(|_| self)
}
pub(crate) fn into_payload(self) -> ErrorPayload {
ErrorPayload {
body: Some(self.to_string()),
err: self,
}
}
}
impl Serialize for Error {
fn serialize<S>(&self, ser: S) -> ::std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = ser.serialize_map(None)?;
map.serialize_entry("code", &self.status_code().as_u16())?;
map.serialize_entry("description", &self.to_string())?;
map.end()
}
}
#[derive(Debug)]
pub(crate) struct ErrorPayload {
body: Option<String>,
err: Error,
}
impl ErrorPayload {
pub(crate) fn into_inner(self) -> Error {
self.err
}
}
impl Payload for ErrorPayload {
type Data = io::Cursor<String>;
type Error = Never;
fn poll_data(&mut self) -> Poll<Option<Self::Data>, Self::Error> {
Ok(self.body.take().map(io::Cursor::new).into())
}
fn poll_trailers(&mut self) -> Poll<Option<HeaderMap>, Self::Error> {
Ok(None.into())
}
fn is_end_stream(&self) -> bool {
self.body.is_none()
}
fn content_length(&self) -> Option<u64> {
self.body.as_ref().map(|body| body.len() as u64)
}
}
pub type Result<T> = ::std::result::Result<T, Error>;
#[derive(Debug)]
struct Failure<F: Fail>(F);
impl<F: Fail> Display for Failure<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl<F: Fail> HttpError for Failure<F> {
fn cause(&self) -> Option<&dyn Fail> {
self.0.cause()
}
}
#[allow(missing_docs)]
pub fn fail(err: impl Fail) -> Error {
Failure(err).into()
}
#[allow(missing_docs)]
pub fn bad_request(msg: impl Debug + Display + Send + Sync + 'static) -> Error {
err_msg(StatusCode::BAD_REQUEST, msg)
}
#[allow(missing_docs)]
pub fn err_msg(status: StatusCode, msg: impl Debug + Display + Send + Sync + 'static) -> Error {
ErrorMessage { status, msg }.into()
}
#[derive(Debug)]
struct ErrorMessage<D: fmt::Debug + fmt::Display + Send + 'static> {
status: StatusCode,
msg: D,
}
impl<D> fmt::Display for ErrorMessage<D>
where
D: fmt::Debug + fmt::Display + Send + 'static,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.msg, f)
}
}
impl<D> HttpError for ErrorMessage<D>
where
D: fmt::Debug + fmt::Display + Send + Sync + 'static,
{
fn status_code(&self) -> StatusCode {
self.status
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
pub enum Never {}
impl Never {
pub fn never_into<T>(self) -> T {
match self {}
}
}
impl fmt::Display for Never {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {}
}
}
impl error::Error for Never {
fn description(&self) -> &str {
match *self {}
}
fn cause(&self) -> Option<&dyn error::Error> {
match *self {}
}
}
impl HttpError for Never {}