/*! Iterate over error `.source()` chains. NOTE: This module is taken wholesale from . */ use std::error::Error as StdError; use std::vec; use ChainState::*; /// Iterator of a chain of source errors. /// /// This type is the iterator returned by [`Report::chain`]. /// /// # Example /// /// ``` /// use miette::Report; /// use std::io; /// /// pub fn underlying_io_error_kind(error: &Report) -> Option { /// for cause in error.chain() { /// if let Some(io_error) = cause.downcast_ref::() { /// return Some(io_error.kind()); /// } /// } /// None /// } /// ``` #[derive(Clone)] #[allow(missing_debug_implementations)] pub struct Chain<'a> { state: crate::chain::ChainState<'a>, } #[derive(Clone)] pub(crate) enum ChainState<'a> { Linked { next: Option<&'a (dyn StdError + 'static)>, }, Buffered { rest: vec::IntoIter<&'a (dyn StdError + 'static)>, }, } impl<'a> Chain<'a> { pub(crate) fn new(head: &'a (dyn StdError + 'static)) -> Self { Chain { state: ChainState::Linked { next: Some(head) }, } } } impl<'a> Iterator for Chain<'a> { type Item = &'a (dyn StdError + 'static); fn next(&mut self) -> Option { match &mut self.state { Linked { next } => { let error = (*next)?; *next = error.source(); Some(error) } Buffered { rest } => rest.next(), } } fn size_hint(&self) -> (usize, Option) { let len = self.len(); (len, Some(len)) } } impl DoubleEndedIterator for Chain<'_> { fn next_back(&mut self) -> Option { match &mut self.state { Linked { mut next } => { let mut rest = Vec::new(); while let Some(cause) = next { next = cause.source(); rest.push(cause); } let mut rest = rest.into_iter(); let last = rest.next_back(); self.state = Buffered { rest }; last } Buffered { rest } => rest.next_back(), } } } impl ExactSizeIterator for Chain<'_> { fn len(&self) -> usize { match &self.state { Linked { mut next } => { let mut len = 0; while let Some(cause) = next { next = cause.source(); len += 1; } len } Buffered { rest } => rest.len(), } } } impl Default for Chain<'_> { fn default() -> Self { Chain { state: ChainState::Buffered { rest: Vec::new().into_iter(), }, } } }