twitch_highway 0.5.2

Twitch API
Documentation
use std::{
    error::Error as StdError,
    fmt::{Debug, Display, Formatter, Result as FmtResult},
};

type BoxError = Box<dyn StdError + Send + Sync>;

pub struct Error {
    inner: Box<Inner>,
}

#[derive(Debug)]
struct Inner {
    kind: Kind,
    message: Option<String>,
    source: Option<BoxError>,
}

#[derive(Debug)]
pub(crate) enum Kind {
    Request,
    Api,
    Parse,
}

impl Display for Kind {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        match self {
            Kind::Request => f.write_str("request failed"),
            Kind::Api => f.write_str("api request failed"),
            Kind::Parse => f.write_str("response deserialization failed"),
        }
    }
}

impl Error {
    pub(crate) fn with_source(kind: Kind, source: impl Into<BoxError>) -> Self {
        Self {
            inner: Box::new(Inner {
                kind,
                message: None,
                source: Some(source.into()),
            }),
        }
    }

    pub(crate) fn with_message(kind: Kind, message: impl Into<String>) -> Self {
        Self {
            inner: Box::new(Inner {
                kind,
                message: Some(message.into()),
                source: None,
            }),
        }
    }

    pub fn message(&self) -> Option<&str> {
        self.inner.message.as_deref()
    }

    pub fn is_request(&self) -> bool {
        matches!(self.inner.kind, Kind::Request)
    }

    pub fn is_api(&self) -> bool {
        matches!(self.inner.kind, Kind::Api)
    }

    pub fn is_parse(&self) -> bool {
        matches!(self.inner.kind, Kind::Parse)
    }
}

impl Debug for Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        let mut builder = f.debug_struct("twitch_highway::Error");

        builder.field("kind", &self.inner.kind);

        if let Some(message) = &self.inner.message {
            builder.field("message", message);
        }

        if let Some(source) = &self.inner.source {
            builder.field("source", source);
        }

        builder.finish()
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        write!(f, "{}", self.inner.kind)?;
        if let Some(msg) = &self.inner.message {
            write!(f, ": {msg}")?;
        }
        if let Some(src) = &self.inner.source {
            write!(f, ": {src}")?;
        }
        Ok(())
    }
}

impl StdError for Error {
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        self.inner.source.as_ref().map(|e| &**e as _)
    }
}

impl From<reqwest::Error> for Error {
    fn from(value: reqwest::Error) -> Self {
        Self::with_source(Kind::Request, value)
    }
}

pub(crate) fn api_error(message: impl Into<String>) -> Error {
    Error::with_message(Kind::Api, message.into())
}

pub(crate) fn parse_error<E: Into<BoxError>>(source: E) -> Error {
    Error::with_source(Kind::Parse, source)
}