errors/
iter.rs

1//! Iterating error source chains
2//!
3//! This contains tools for inspecting error `source` chains.
4//!
5//! There are two `Iterator`s:
6//!
7//! - [`chain`](iter::chain): Iterates over the source chain, including the
8//!   first `Error`.
9//! - [`sources`](iter::sources): Iterates over only the sources of an `Error`,
10//!   excluding itself.
11//!
12//! There also a few utilities for quickly traversing a source chain with a
13//! specific goal in mind.
14//!
15//! - [`root`](iter::root): Finds the root source for a given error.
16//! - [`is`](iter::is): Checks a source chain if it contains a given type.
17//! - [`find`](iter::find): Finds the first occurance of a type in a source
18//!   chain.
19
20use super::{Error, ErrorRef};
21
22/// Get an `Iterator` of the whole chain of errors.
23///
24/// Includes the `err` in the iterator as the first item.
25///
26/// # Example
27///
28/// ```
29/// let err = errors::wrap("c", errors::wrap("b", "a"));
30///
31/// let expected = ["c", "b", "a"];
32///
33/// for (err, &s) in errors::iter::chain(&err).zip(expected.iter()) {
34///     assert_eq!(err.to_string(), s);
35/// }
36/// ```
37pub fn chain<'a>(err: &'a ErrorRef) -> impl Iterator<Item = &'a ErrorRef> + 'a {
38    Iter { err: Some(err) }
39}
40
41/// Get an `Iterator` of the source chain of this error.
42///
43/// Skips `err`, starting as `err.source()`. Equivalent to `chain(err).skip(1)`.
44///
45/// # Example
46///
47/// ```
48/// let err = errors::wrap("c", errors::wrap("b", "a"));
49///
50///
51/// let expected = ["b", "a"];
52///
53/// for (err, &s) in errors::iter::sources(&err).zip(expected.iter()) {
54///     assert_eq!(err.to_string(), s);
55/// }
56/// ```
57pub fn sources(err: &dyn Error) -> impl Iterator<Item = &ErrorRef> {
58    Iter { err: err.source() }
59}
60
61/// Returns whether the error source chain contains a given type.
62///
63/// # Example
64///
65/// ```
66/// use std::io;
67///
68/// let err1 = io::Error::new(io::ErrorKind::Other, "boom");
69/// let err2 = errors::wrap("ruh roh", err1);
70///
71/// let io = errors::find::<io::Error>(&err2).unwrap();
72/// ```
73pub fn find<E: Error + 'static>(err: &ErrorRef) -> Option<&E> {
74    chain(err)
75        .find_map(|e| e.downcast_ref::<E>())
76}
77
78/// Returns whether the error source chain contains a given type.
79///
80/// # Example
81///
82/// ```
83/// use std::io;
84///
85/// let err1 = io::Error::new(io::ErrorKind::Other, "boom");
86/// assert!(errors::is::<io::Error>(&err1));
87///
88/// let err2 = errors::wrap("ruh roh", err1);
89/// assert!(errors::is::<io::Error>(&err2));
90/// ```
91pub fn is<E: Error + 'static>(err: &ErrorRef) -> bool {
92    chain(err)
93        .any(|e| e.is::<E>())
94}
95
96/// Get the root source of an `Error`.
97///
98/// If the provided `Error` has a source chain, this will find the last one
99/// in the chain. If there is no chain, returns the same `Error`.
100///
101/// # Example
102///
103/// ```
104/// // Error chain: c -> b -> a (root)
105/// let err = errors::wrap("c", errors::wrap("b", "a"));
106///
107/// assert_eq!(errors::iter::root(&err).to_string(), "a");
108///
109/// // No chain:
110/// let root = errors::new("ninja cat");
111///
112/// assert_eq!(errors::iter::root(&root).to_string(), "ninja cat");
113/// ```
114pub fn root(err: &ErrorRef) -> &ErrorRef {
115    chain(err)
116        .last()
117        .expect("errors::iter::chain always yields at least 1 item")
118}
119
120struct Iter<'a> {
121    err: Option<&'a ErrorRef>,
122}
123
124impl<'a> Iterator for Iter<'a> {
125    type Item = &'a ErrorRef;
126
127    fn next(&mut self) -> Option<Self::Item> {
128        let next = self.err?;
129        self.err = next.source();
130        Some(next)
131    }
132}