errors/
fmt.rs

1//! Utilities for formatting `Error`s.
2
3use std::fmt as std_fmt;
4use super::{BoxError, Error};
5
6/// An adapter to pretty-print an error source chain.
7///
8/// # Example
9///
10/// ```no_run
11/// fn main() -> Result<(), errors::Main> {
12///     // Any program that returns a normal `impl Error`
13///     Err("ruh roh")?;
14///
15///     Ok(())
16/// }
17/// ```
18pub struct Main(BoxError);
19
20impl std_fmt::Debug for Main {
21    fn fmt(&self, f: &mut std_fmt::Formatter) -> std_fmt::Result {
22        let err = crate::new::wrap_ref(&*self.0);
23        write!(f, "{:+#}", err)
24    }
25}
26
27impl<E: Into<BoxError>> From<E> for Main {
28    fn from(err: E) -> Main {
29        Main(err.into())
30    }
31}
32
33/// Create a `Display` adapter that applies the formatting rules to any error.
34///
35/// # Example
36///
37/// ```
38/// use std::io;
39///
40/// let orig = errors::wrap("exploded", "cat hair in generator");
41/// let err = io::Error::new(io::ErrorKind::Other, orig);
42///
43/// // Foreign type might not know how to format sources...
44/// // But now it does!
45/// assert_eq!(
46///     format!("{:+}", errors::fmt(&err)),
47///     "exploded: cat hair in generator"
48/// );
49/// ```
50pub fn fmt<'a>(err: &'a dyn Error) -> impl std_fmt::Display + 'a {
51    ::new::wrap_ref(err)
52}
53
54#[cfg(test)]
55mod tests {
56    use std::fmt;
57    use std::io;
58
59    use {BoxError, Error};
60
61    #[derive(Debug)]
62    struct Naive(Option<BoxError>);
63
64    impl fmt::Display for Naive {
65        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66            "naive".fmt(f)
67        }
68    }
69
70    impl Error for Naive {
71        fn source(&self) -> Option<&(dyn Error + 'static)> {
72            self.0.as_ref().map(|e| &**e as _)
73        }
74    }
75
76    #[test]
77    fn chain_wraps_outside_errors() {
78        let a = "a";
79
80        // root
81        let err = io::Error::new(io::ErrorKind::Other, a);
82        assert_eq!(format!("{}", super::fmt(&err)), a);
83        assert_eq!(format!("{:.0}", super::fmt(&err)), a);
84        assert_eq!(format!("{:+}", super::fmt(&err)), a);
85        assert_eq!(format!("{:+.0}", super::fmt(&err)), a);
86
87        // nest 1
88        let err = Naive(Some(err.into()));
89        let naive = "naive";
90        let naive_a = "naive: a";
91        assert_eq!(format!("{}", super::fmt(&err)), naive);
92        assert_eq!(format!("{:.0}", super::fmt(&err)), naive);
93        assert_eq!(format!("{:+}", super::fmt(&err)), naive_a);
94        assert_eq!(format!("{:+.0}", super::fmt(&err)), naive);
95        assert_eq!(format!("{:+.1}", super::fmt(&err)), naive_a);
96    }
97
98    #[test]
99    fn chain_wraps_our_errors() {
100        let err = ::wrap("b", "a");
101        let b = "b";
102        let b_a = "b: a";
103        assert_eq!(format!("{}", super::fmt(&err)), b);
104        assert_eq!(format!("{:.0}", super::fmt(&err)), b);
105        assert_eq!(format!("{:+}", super::fmt(&err)), b_a);
106        assert_eq!(format!("{:+.0}", super::fmt(&err)), b);
107        assert_eq!(format!("{:+.1}", super::fmt(&err)), b_a);
108    }
109
110    /// Simulate an error type that by default prefers to show one level
111    /// deep in its source chain, but wants to opt-in to behaving correctly
112    /// with `errors::fmt`.
113    #[derive(Debug)]
114    struct OneDeep(BoxError);
115
116    impl fmt::Display for OneDeep {
117        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118            if f.sign_minus() {
119                write!(f, "one deep")
120            } else {
121                write!(f, "one deep: {}", self.0)
122            }
123        }
124    }
125
126    impl Error for OneDeep {
127        fn source(&self) -> Option<&(dyn Error + 'static)> {
128            Some(&*self.0)
129        }
130    }
131
132    #[test]
133    fn one_deep_is_passed_minus() {
134        let orig = ::new("a");
135        let one_deep = OneDeep(orig.into());
136
137        assert_eq!(format!("{}", one_deep), "one deep: a");
138
139        let err = ::wrap("b", one_deep);
140        let b = "b";
141        let b_1 = "b: one deep";
142        let b_1_a = "b: one deep: a";
143        assert_eq!(format!("{}", err), b);
144        assert_eq!(format!("{:.0}", err), b);
145        assert_eq!(format!("{:+}", err), b_1_a);
146        assert_eq!(format!("{:+.0}", err), b);
147        assert_eq!(format!("{:+.1}", err), b_1);
148    }
149
150    #[test]
151    fn one_deep_opaque_is_passed_minus() {
152        let orig = ::new("a");
153        let one_deep = ::opaque(OneDeep(orig.into()));
154
155        assert_eq!(format!("{}", one_deep), "one deep: a");
156
157        let err = ::wrap("b", one_deep);
158        let b = "b";
159        let b_1 = "b: one deep";
160        let b_1_a = "b: one deep: a";
161        assert_eq!(format!("{}", err), b);
162        assert_eq!(format!("{:.0}", err), b);
163        assert_eq!(format!("{:+}", err), b_1_a);
164        assert_eq!(format!("{:+.0}", err), b);
165        assert_eq!(format!("{:+.1}", err), b_1);
166    }
167}