use failure::{Backtrace, Context, Error, Fail};
use std::fmt;

#[derive(Debug)]
pub struct FeedParserError {
    inner: Context<FeedParserErrorKind>,
}

#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
pub enum FeedParserErrorKind {
    #[fail(display = "Failed to parse an Url")]
    Url,
    #[fail(display = "Failed to parse bytes to valid utf8")]
    Utf8,
    #[fail(display = "Http request failed")]
    Http,
    #[fail(display = "Failed to parse feed url from HTML")]
    Html,
    #[fail(display = "Failed to parse feed")]
    Feed,
    #[fail(display = "Unknown Error")]
    Unknown,
}

impl Fail for FeedParserError {
    fn cause(&self) -> Option<&dyn Fail> {
        self.inner.cause()
    }

    fn backtrace(&self) -> Option<&Backtrace> {
        self.inner.backtrace()
    }
}

impl fmt::Display for FeedParserError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.inner, f)
    }
}

impl FeedParserError {
    #[allow(dead_code)]
    pub fn kind(&self) -> FeedParserErrorKind {
        *self.inner.get_context()
    }
}

impl From<FeedParserErrorKind> for FeedParserError {
    fn from(kind: FeedParserErrorKind) -> FeedParserError {
        FeedParserError { inner: Context::new(kind) }
    }
}

impl From<Context<FeedParserErrorKind>> for FeedParserError {
    fn from(inner: Context<FeedParserErrorKind>) -> FeedParserError {
        FeedParserError { inner }
    }
}

impl From<Error> for FeedParserError {
    fn from(_: Error) -> FeedParserError {
        FeedParserError {
            inner: Context::new(FeedParserErrorKind::Unknown),
        }
    }
}
