1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
//! Iterators over `std::error::Error` sources on stable Rust.
//!
//! ```
//! use error_iter::ErrorIter as _;
//! use std::io::{Error as IoError, ErrorKind};
//! use thiserror::Error;
//!
//! #[derive(Debug, Error)]
//! enum Error {
//! #[error("I/O Error")]
//! Io(#[from] IoError),
//!
//! #[error("Unknown error")]
//! Unknown,
//! }
//!
//! fn do_something() {
//! let error = Error::from(IoError::new(ErrorKind::Other, "oh no!"));
//!
//! eprintln!("Error: {}", error);
//! for source in error.sources().skip(1) {
//! eprintln!(" Caused by: {}", source);
//! }
//! }
//! ```
#![deny(clippy::all)]
#![deny(clippy::pedantic)]
#![forbid(unsafe_code)]
pub struct ErrorIterator<'a> {
inner: Option<&'a (dyn std::error::Error + 'static)>,
}
impl<'a> Iterator for ErrorIterator<'a> {
type Item = &'a (dyn std::error::Error + 'static);
fn next(&mut self) -> Option<Self::Item> {
if let Some(error) = self.inner.take() {
self.inner = error.source();
return Some(error);
}
None
}
}
/// Implement this trait on your error types for free iterators over their sources!
///
/// The default implementation provides iterators for any type that implements `std::error::Error`.
pub trait ErrorIter: std::error::Error + Sized + 'static {
/// Create an iterator over the error and its recursive sources.
///
/// ```
/// use error_iter::ErrorIter as _;
/// use thiserror::Error;
///
/// #[derive(Debug, Error)]
/// enum Error {
/// #[error("Nested error: {0}")]
/// Nested(#[source] Box<Error>),
///
/// #[error("Leaf error")]
/// Leaf,
/// }
///
/// let error = Error::Nested(Box::new(Error::Leaf));
///
/// let mut iter = error.sources();
///
/// assert_eq!("Nested error: Leaf error".to_string(), iter.next().unwrap().to_string());
/// assert_eq!("Leaf error".to_string(), iter.next().unwrap().to_string());
/// assert!(iter.next().is_none());
/// assert!(iter.next().is_none());
/// ```
fn sources(&self) -> ErrorIterator {
ErrorIterator { inner: Some(self) }
}
}
impl<T> ErrorIter for T where T: std::error::Error + Sized + 'static {}
#[cfg(test)]
mod tests {
use super::*;
use thiserror::Error;
#[derive(Debug, Error)]
enum Error {
#[error("Nested error: {0}")]
Nested(#[source] Box<Error>),
#[error("Leaf error")]
Leaf,
}
#[test]
fn iter_sources_ok() {
let error = Error::Nested(Box::new(Error::Nested(Box::new(Error::Leaf))));
let mut iter = error.sources();
assert_eq!(
"Nested error: Nested error: Leaf error".to_string(),
iter.next().unwrap().to_string()
);
assert_eq!(
"Nested error: Leaf error".to_string(),
iter.next().unwrap().to_string()
);
assert_eq!("Leaf error".to_string(), iter.next().unwrap().to_string());
assert!(iter.next().is_none());
assert!(iter.next().is_none());
}
}