failure/
context.rs

1use core::fmt::{self, Debug, Display};
2
3use Fail;
4
5without_std! {
6    /// An error with context around it.
7    ///
8    /// The context is intended to be a human-readable, user-facing explanation for the
9    /// error that has occurred. The underlying error is not assumed to be end-user-relevant
10    /// information.
11    ///
12    /// The `Display` impl for `Context` only prints the human-readable context, while the
13    /// `Debug` impl also prints the underlying error.
14    pub struct Context<D: Display + Send + Sync + 'static> {
15        context: D,
16    }
17
18    impl<D: Display + Send + Sync + 'static> Context<D> {
19        /// Creates a new context without an underlying error message.
20        pub fn new(context: D) -> Context<D> {
21            Context { context }
22        }
23
24        /// Returns a reference to the context provided with this error.
25        pub fn get_context(&self) -> &D {
26            &self.context
27        }
28
29        /// Maps `Context<D>` to `Context<T>` by applying a function to the contained context.
30        pub fn map<F, T>(self, op: F) -> Context<T>
31            where F: FnOnce(D) -> T,
32                  T: Display + Send + Sync + 'static
33        {
34            Context {
35                context: op(self.context),
36            }
37        }
38
39        pub(crate) fn with_err<E: Fail>(context: D, _: E) -> Context<D> {
40            Context { context }
41        }
42    }
43
44    impl<D: Display + Send + Sync + 'static> Fail for Context<D> { }
45
46    impl<D: Display + Send + Sync + 'static> Debug for Context<D> {
47        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48            write!(f, "{}", self.context)
49        }
50    }
51
52    impl<D: Display + Send + Sync + 'static> Display for Context<D> {
53        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54            write!(f, "{}", self.context)
55        }
56    }
57
58    #[test]
59    fn test_map() {
60        let ctx = Context::new("a string").map(|s| format!("{} with some more stuff", s));
61        assert_eq!(ctx.context, String::from("a string with some more stuff"));
62    }
63}
64
65with_std! {
66    use {Error, Backtrace};
67
68    /// An error with context around it.
69    ///
70    /// The context is intended to be a human-readable, user-facing explanation for the
71    /// error that has occurred. The underlying error is not assumed to be end-user-relevant
72    /// information.
73    ///
74    /// The `Display` impl for `Context` only prints the human-readable context, while the
75    /// `Debug` impl also prints the underlying error.
76    pub struct Context<D: Display + Send + Sync + 'static> {
77        context: D,
78        failure: Either<Backtrace, Error>,
79    }
80
81    impl<D: Display + Send + Sync + 'static> Context<D> {
82        /// Creates a new context without an underlying error message.
83        pub fn new(context: D) -> Context<D> {
84            let failure = Either::This(Backtrace::new());
85            Context { context, failure }
86        }
87
88        /// Returns a reference to the context provided with this error.
89        pub fn get_context(&self) -> &D {
90            &self.context
91        }
92
93        /// Maps `Context<D>` to `Context<T>` by applying a function to the contained context.
94        pub fn map<F, T>(self, op: F) -> Context<T>
95            where F: FnOnce(D) -> T,
96                  T: Display + Send + Sync + 'static
97        {
98            Context {
99                context: op(self.context),
100                failure: self.failure,
101            }
102        }
103
104        pub(crate) fn with_err<E: Into<Error>>(context: D, error: E) -> Context<D> {
105            let failure = Either::That(error.into());
106            Context { context, failure }
107        }
108    }
109
110    impl<D: Display + Send + Sync + 'static> Fail for Context<D> {
111        fn name(&self) -> Option<&str> {
112            self.failure.as_cause().and_then(|x| x.name())
113        }
114
115        fn cause(&self) -> Option<&dyn Fail> {
116            self.failure.as_cause()
117        }
118
119        fn backtrace(&self) -> Option<&Backtrace> {
120            Some(self.failure.backtrace())
121        }
122    }
123
124    impl<D: Display + Send + Sync + 'static> Debug for Context<D> {
125        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
126            write!(f, "{:?}\n\n{}", self.failure, self.context)
127        }
128    }
129
130    impl<D: Display + Send + Sync + 'static> Display for Context<D> {
131        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
132            write!(f, "{}", self.context)
133        }
134    }
135
136    enum Either<A, B> {
137        This(A),
138        That(B),
139    }
140
141    impl Either<Backtrace, Error> {
142        fn backtrace(&self) -> &Backtrace {
143            match *self {
144                Either::This(ref backtrace) => backtrace,
145                Either::That(ref error)     => error.backtrace(),
146            }
147        }
148
149        fn as_cause(&self) -> Option<&dyn Fail> {
150            match *self {
151                Either::This(_)         => None,
152                Either::That(ref error) => Some(error.as_fail())
153            }
154        }
155    }
156
157    impl Debug for Either<Backtrace, Error> {
158        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159            match *self {
160                Either::This(ref backtrace) => write!(f, "{:?}", backtrace),
161                Either::That(ref error)     => write!(f, "{:?}", error),
162            }
163        }
164    }
165
166    #[test]
167    fn test_map() {
168        let ctx = Context::new("a string").map(|s| format!("{} with some more stuff", s));
169        assert_eq!(ctx.context, String::from("a string with some more stuff"));
170    }
171}
172
173impl<D> From<D> for Context<D>
174where
175    D: Display + Send + Sync + 'static,
176{
177    fn from(display: D) -> Context<D> {
178        Context::new(display)
179    }
180}