Skip to main content

rust_ev_verifier_lib/
error_utils.rs

1use std::fmt::Display;
2
3/// An iterator over an Error and its sources.
4///
5/// If you want to omit the initial error and only process its sources, use `skip(1)`.
6#[derive(Debug, Clone)]
7pub struct ErrorChain<'a, 'b> {
8    inner: Option<&'a (dyn std::error::Error + 'b)>,
9}
10
11impl<'a, 'b> ErrorChain<'a, 'b> {
12    /// Creates a new error chain iterator.
13    pub fn new(error: &'a (dyn std::error::Error + 'b)) -> Self {
14        ErrorChain { inner: Some(error) }
15    }
16}
17
18impl<'a, 'b> Iterator for ErrorChain<'a, 'b> {
19    type Item = &'a (dyn std::error::Error + 'b);
20
21    fn next(&mut self) -> Option<Self::Item> {
22        match self.inner {
23            None => None,
24            Some(e) => {
25                self.inner = e.source();
26                Some(e)
27            }
28        }
29    }
30}
31
32#[derive(Debug, Clone)]
33pub struct Report<'a> {
34    inner: &'a dyn std::error::Error,
35}
36
37impl<'a> Report<'a> {
38    pub fn new(error: &'a dyn std::error::Error) -> Self {
39        Self { inner: error }
40    }
41}
42
43impl<'a> Display for Report<'a> {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        let error_str = ErrorChain::new(self.inner)
46            .map(|e| e.to_string())
47            .collect::<Vec<_>>();
48        let mut res = vec![self.inner.to_string()];
49        if error_str.len() > 1 {
50            res.push("backtrace:".to_string());
51            res.append(
52                &mut error_str
53                    .iter()
54                    .enumerate()
55                    .map(|(i, s)| format!("{i}: {s}"))
56                    .collect(),
57            );
58        }
59        write!(f, "{}", res.join("\n"))
60    }
61}
62
63#[cfg(test)]
64mod test {
65    use super::*;
66    use thiserror::Error;
67
68    #[derive(Error, Debug)]
69    #[error("inner 21 error")]
70    struct Inner21 {}
71
72    #[derive(Error, Debug)]
73    #[error("inner 22 error")]
74    struct Inner22 {}
75
76    #[derive(Error, Debug)]
77    enum Inner {
78        #[error("Context 21")]
79        Inner21 { source: Inner21 },
80        #[error("Context 22")]
81        Inner22 { source: Inner22 },
82    }
83
84    #[derive(Error, Debug)]
85    enum Outer {
86        #[error("Context Inner")]
87        Inner { source: Inner },
88    }
89
90    #[test]
91    fn test_iter() {
92        let e = Outer::Inner {
93            source: Inner::Inner21 { source: Inner21 {} },
94        };
95        let res = ErrorChain::new(&e)
96            .map(|e| e.to_string())
97            .collect::<Vec<_>>();
98        assert_eq!(
99            res,
100            vec![
101                "Context Inner".to_string(),
102                "Context 21".to_string(),
103                "inner 21 error".to_string()
104            ]
105        )
106    }
107
108    #[test]
109    fn test_iter2() {
110        let e = Outer::Inner {
111            source: Inner::Inner22 { source: Inner22 {} },
112        };
113        let res = ErrorChain::new(&e)
114            .map(|e| e.to_string())
115            .collect::<Vec<_>>();
116        assert_eq!(
117            res,
118            vec![
119                "Context Inner".to_string(),
120                "Context 22".to_string(),
121                "inner 22 error".to_string()
122            ]
123        )
124    }
125}