aws_smithy_types/error/
display.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Error wrapper that displays error context
7
8use std::error::Error;
9use std::fmt;
10
11/// Provides a `Display` impl for an `Error` that outputs the full error context
12///
13/// This utility follows the error cause/source chain and displays every error message
14/// in the chain separated by ": ". At the end of the chain, it outputs a debug view
15/// of the entire error chain.
16///
17/// # Example
18///
19/// ```no_run
20/// # let err: &dyn std::error::Error = unimplemented!();
21/// # use aws_smithy_types::error::display::DisplayErrorContext;
22/// println!("There was an unhandled error: {}", DisplayErrorContext(&err));
23/// ```
24///
25// Internally in the SDK, this is useful for emitting errors with `tracing` in cases
26// where the error is not returned back to the customer.
27#[derive(Debug)]
28pub struct DisplayErrorContext<E: Error>(
29    /// The error to display full context for
30    pub E,
31);
32
33impl<E: Error> fmt::Display for DisplayErrorContext<E> {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        write_err(f, &self.0)?;
36        // Also add a debug version of the error at the end
37        write!(f, " ({:?})", self.0)
38    }
39}
40
41fn write_err(f: &mut fmt::Formatter<'_>, err: &dyn Error) -> fmt::Result {
42    write!(f, "{}", err)?;
43    if let Some(source) = err.source() {
44        write!(f, ": ")?;
45        write_err(f, source)?;
46    }
47    Ok(())
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    use std::error::Error;
54    use std::fmt;
55
56    #[derive(Debug)]
57    struct TestError {
58        what: &'static str,
59        source: Option<Box<dyn Error>>,
60    }
61
62    impl fmt::Display for TestError {
63        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64            write!(f, "{}", self.what)
65        }
66    }
67
68    impl Error for TestError {
69        fn source(&self) -> Option<&(dyn Error + 'static)> {
70            self.source.as_deref()
71        }
72    }
73
74    #[test]
75    fn no_sources() {
76        assert_eq!(
77            "test (TestError { what: \"test\", source: None })",
78            format!(
79                "{}",
80                DisplayErrorContext(TestError {
81                    what: "test",
82                    source: None
83                })
84            )
85        );
86    }
87
88    #[test]
89    fn sources() {
90        assert_eq!(
91            "foo: bar: baz (TestError { what: \"foo\", source: Some(TestError { what: \"bar\", source: Some(TestError { what: \"baz\", source: None }) }) })",
92            format!(
93                "{}",
94                DisplayErrorContext(TestError {
95                    what: "foo",
96                    source: Some(Box::new(TestError {
97                        what: "bar",
98                        source: Some(Box::new(TestError {
99                            what: "baz",
100                            source: None
101                        }))
102                    }) as Box<_>)
103                })
104            )
105        );
106    }
107}