use std::{io, sync::Arc};
pub struct ErrorSources<'a> {
error: Option<&'a (dyn std::error::Error + 'static)>,
}
impl<'a> ErrorSources<'a> {
pub fn new(error: &'a (dyn std::error::Error + 'static)) -> Self {
Self { error: Some(error) }
}
}
impl<'a> Iterator for ErrorSources<'a> {
type Item = &'a (dyn std::error::Error + 'static);
fn next(&mut self) -> Option<Self::Item> {
let error = self.error.take()?;
if let Some(io_error) = error.downcast_ref::<io::Error>() {
self.error = io_error.get_ref().map(|e| e as _);
} else if let Some(io_error) = error.downcast_ref::<Arc<io::Error>>() {
self.error = io_error.get_ref().map(|e| e as _);
} else {
self.error = error.source();
}
Some(error)
}
}
#[cfg(test)]
mod test {
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_time_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
use super::*;
#[derive(thiserror::Error, Debug)]
#[error("my error")]
struct MyError;
macro_rules! downcast_next {
($errors:expr, $ty:ty) => {
$errors.next().unwrap().downcast_ref::<$ty>().unwrap()
};
}
#[test]
fn error_sources() {
let wrapped_error = io::Error::new(
io::ErrorKind::ConnectionReset,
Arc::new(io::Error::new(io::ErrorKind::ConnectionReset, MyError)),
);
let mut errors = ErrorSources::new(&wrapped_error);
downcast_next!(errors, io::Error);
downcast_next!(errors, Arc<io::Error>);
downcast_next!(errors, MyError);
assert!(errors.next().is_none());
}
}