Skip to main content

mm1_common/errors/
chain.rs

1use std::error::Error as StdError;
2use std::fmt;
3
4use crate::types::AnyError;
5
6pub trait ExactTypeDisplayChainExt {
7    fn as_display_chain(&self) -> impl fmt::Display + fmt::Debug;
8}
9
10pub trait StdErrorDisplayChainExt: StdError + Sized {
11    fn as_display_chain(&self) -> impl fmt::Display + fmt::Debug {
12        let e: &dyn StdError = self;
13        D(e)
14    }
15}
16
17impl<E> StdErrorDisplayChainExt for E where E: StdError {}
18
19impl<T> ExactTypeDisplayChainExt for T
20where
21    for<'a> D<&'a T>: fmt::Display + fmt::Debug,
22{
23    fn as_display_chain(&self) -> impl fmt::Display + fmt::Debug {
24        D(self)
25    }
26}
27
28struct D<T>(T);
29
30impl fmt::Display for D<&dyn StdError> {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        let mut e = self.0;
33        write!(f, "{}", e)?;
34        while let Some(source) = e.source() {
35            write!(f, " << {}", source)?;
36            e = source;
37        }
38        Ok(())
39    }
40}
41impl fmt::Debug for D<&dyn StdError> {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        fmt::Display::fmt(self, f)
44    }
45}
46
47impl fmt::Display for D<&AnyError> {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        let mut chain = self.0.chain();
50        if let Some(e) = chain.next() {
51            write!(f, "{}", e)?;
52        }
53        for source in chain {
54            write!(f, "<< {}", source)?;
55        }
56
57        Ok(())
58    }
59}
60
61impl fmt::Debug for D<&AnyError> {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        fmt::Display::fmt(self, f)
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[derive(Debug, thiserror::Error)]
72    #[error("inner")]
73    struct Inner;
74
75    #[derive(Debug, thiserror::Error)]
76    #[error("outer")]
77    struct Outer(#[source] Inner);
78
79    #[test]
80    fn std_error_display_chain_joins_sources() {
81        let e = Outer(Inner);
82        assert_eq!(e.as_display_chain().to_string(), "outer << inner");
83    }
84}