display_error_chain/
result_ext.rs

1use std::{error::Error, fmt};
2
3use crate::DisplayErrorChain;
4
5/// A helper extension trait to "unwrap" results with an error chain.
6pub trait ResultExt<T, E> {
7    /// Like [`Result::unwrap`][Result::unwrap], but wraps the error in the
8    /// [DisplayErrorChain] and prints out the full chain in case an `Err(_)`
9    /// value is encountered.
10    #[track_caller]
11    fn unwrap_chain(self) -> T;
12
13    /// Like [`Result::expect`][Result::expect], but wraps the error in the
14    /// [DisplayErrorChain] and prints out the full chain in case an `Err(_)`
15    /// value is encountered.
16    #[track_caller]
17    fn expect_chain(self, msg: &str) -> T;
18}
19
20impl<T, E: Error> ResultExt<T, E> for Result<T, E> {
21    #[inline]
22    #[track_caller]
23    fn unwrap_chain(self) -> T {
24        match self {
25            Ok(value) => value,
26            Err(e) => unwrap_failed(
27                "called `Result::unwrap_chain()` on an `Err` value",
28                &DisplayErrorChain::new(&e),
29            ),
30        }
31    }
32    #[inline]
33    #[track_caller]
34    fn expect_chain(self, msg: &str) -> T {
35        match self {
36            Ok(value) => value,
37            Err(e) => unwrap_failed(msg, &DisplayErrorChain::new(&e)),
38        }
39    }
40}
41
42// This is a separate function to reduce the code size of the methods
43#[inline(never)]
44#[cold]
45#[track_caller]
46fn unwrap_failed(msg: &str, error: &dyn fmt::Display) -> ! {
47    panic!("{}: {}", msg, error)
48}
49
50#[cfg(test)]
51mod test {
52    use super::ResultExt;
53
54    macro_rules! impl_error {
55        ($ty:ty, $display:expr, $source:expr) => {
56            impl ::std::fmt::Display for $ty {
57                fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
58                    write!(f, "{}", $display)
59                }
60            }
61
62            impl ::std::error::Error for $ty {
63                fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> {
64                    $source
65                }
66            }
67        };
68    }
69
70    // `TopLevel` is caused by a `MidLevel`.
71    #[derive(Debug)]
72    struct TopLevel;
73    impl_error!(TopLevel, "top level", Some(&MidLevel));
74
75    // `MidLevel` is caused by a `LowLevel`.
76    #[derive(Debug)]
77    struct MidLevel;
78    impl_error!(MidLevel, "mid level", Some(&LowLevel));
79
80    // `LowLevel` is the cause itself.
81    #[derive(Debug)]
82    struct LowLevel;
83    impl_error!(LowLevel, "low level", None);
84
85    #[test]
86    #[should_panic(expected = "\
87    called `Result::unwrap_chain()` on an `Err` value: top level\n\
88Caused by:
89  -> mid level
90  -> low level")]
91    fn test_unwrap() {
92        Err::<(), _>(TopLevel).unwrap_chain();
93    }
94
95    #[test]
96    #[should_panic(expected = "\
97    Some message: top level\n\
98Caused by:
99  -> mid level
100  -> low level")]
101    fn test_expect() {
102        Err::<(), _>(TopLevel).expect_chain("Some message");
103    }
104}