httpio 0.2.4

A transport-agnostic, async HTTP/1.1 client library for any runtime.
Documentation
use core::fmt;
use std::convert::Infallible;
use std::error::Error;
use std::io;
use std::num::ParseIntError;
use std::str::Utf8Error;
use std::string::FromUtf8Error;

#[derive(Debug, Clone)]
pub enum HttpErrorKind {
    Unimplemented,
    UnsupportedCharset,
    UnsupportedContentEncoding,
    ForbiddenOperation,
    UnexpectedTransferEncodingSequence,
    Parse,
    Inner,
}

#[derive(Debug)]
pub struct HttpError {
    pub source: Option<Box<dyn Error + Send + Sync>>,
    pub kind: HttpErrorKind,
}

impl HttpError {
    pub fn new_inner(source: Box<dyn Error + Send + Sync>) -> Self {
        HttpError {
            source: Some(source),
            kind: HttpErrorKind::Inner,
        }
    }

    pub fn new_kind(kind: HttpErrorKind) -> Self {
        HttpError { source: None, kind }
    }

    pub fn is_io_error(&self) -> bool {
        if !matches!(self.kind, HttpErrorKind::Inner) {
            return false;
        }
        matches!(self.source().unwrap().downcast_ref::<io::Error>(), Some(_))
    }
}

impl Error for HttpError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        self.source.as_deref().map(|e| e as &(dyn Error + 'static))
    }
}

impl HttpErrorKind {
    pub fn into_boxed_error(self) -> Box<dyn Error + Send + Sync> {
        let error: HttpError = self.into();
        error.into()
    }
}

impl fmt::Display for HttpErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            HttpErrorKind::Unimplemented => write!(f, "not implemented"),
            HttpErrorKind::UnsupportedCharset => write!(f, "unsupported chearset found"),
            HttpErrorKind::UnsupportedContentEncoding => {
                write!(f, "unsupported content-encoding value found")
            }
            HttpErrorKind::ForbiddenOperation => write!(f, "forbidden operation"),
            HttpErrorKind::UnexpectedTransferEncodingSequence => {
                write!(f, "unexpected transfer-encoding sequence found")
            }
            HttpErrorKind::Parse => write!(f, "unexpected value"),
            HttpErrorKind::Inner => write!(f, "inner error in HttpError::source"),
        }
    }
}

impl fmt::Display for HttpError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.kind {
            HttpErrorKind::Inner => write!(f, "{}", self.source.as_ref().unwrap()),
            _ => write!(f, "{}", self.kind),
        }
    }
}

impl From<HttpErrorKind> for HttpError {
    fn from(kind: HttpErrorKind) -> Self {
        Self::new_kind(kind)
    }
}

impl From<Box<dyn Error + Send + Sync>> for HttpError {
    fn from(err: Box<dyn Error + Send + Sync>) -> Self {
        Self::new_inner(err)
    }
}

impl From<io::Error> for HttpError {
    fn from(error: io::Error) -> Self {
        Self::new_inner(error.into())
    }
}

impl From<ParseIntError> for HttpError {
    fn from(error: ParseIntError) -> Self {
        Self::new_inner(error.into())
    }
}

impl From<FromUtf8Error> for HttpError {
    fn from(error: FromUtf8Error) -> Self {
        Self::new_inner(error.into())
    }
}

impl From<Utf8Error> for HttpError {
    fn from(error: Utf8Error) -> Self {
        Self::new_inner(error.into())
    }
}

impl From<Infallible> for HttpError {
    fn from(_: Infallible) -> Self {
        unreachable!()
    }
}