use super::errors::*;

use std::error::*;

//
// ErrorSourceIterator
//

/// Iterator for an [Error] and its [source](Error::source) chain.
///
/// Will always contains at least the initial error itself.
///
/// Note that this type can be used independently of Problemo.
#[derive(Clone)]
pub struct ErrorSourceIterator<'inner> {
    inner: Option<&'inner (dyn 'static + Error)>,
}

impl<'inner> ErrorSourceIterator<'inner> {
    /// Constructor.
    pub fn new(inner: &'inner (dyn 'static + Error)) -> Self {
        Self { inner: Some(inner) }
    }
}

impl<'inner> Iterator for ErrorSourceIterator<'inner> {
    type Item = &'inner (dyn 'static + Error);

    fn next(&mut self) -> Option<Self::Item> {
        let error = self.inner;

        if let Some(error) = self.inner {
            self.inner = error.source();
        }

        error
    }
}

impl<'inner> Errors for ErrorSourceIterator<'inner> {
    fn iter_errors_of_type<'this, ErrorT>(&'this self) -> impl Iterator<Item = &'this ErrorT>
    where
        ErrorT: 'static + Error,
    {
        self.clone()
            .filter_map(|error| error.downcast_ref::<ErrorT>())
    }
}

//
// IterateErrorSources
//

/// Iterate the [Error::source] chain.
pub trait IterateErrorSources {
    /// Iterate the [Error::source] chain.
    fn iter_sources<'this>(&'this self) -> ErrorSourceIterator<'this>;
}

impl<ErrorT> IterateErrorSources for ErrorT
where
    ErrorT: 'static + Error,
{
    fn iter_sources<'this>(&'this self) -> ErrorSourceIterator<'this> {
        ErrorSourceIterator::new(self)
    }
}
