error_chain/
error_chain.rs

1/// Prefer to use `error_chain` instead of this macro.
2#[doc(hidden)]
3#[macro_export]
4macro_rules! impl_error_chain_processed {
5    // Default values for `types`.
6    (
7        types {}
8        $( $rest: tt )*
9    ) => {
10        impl_error_chain_processed! {
11            types {
12                Error, ErrorKind, ResultExt, Result;
13            }
14            $( $rest )*
15        }
16    };
17    // With `Result` wrapper.
18    (
19        types {
20            $error_name:ident, $error_kind_name:ident,
21            $result_ext_name:ident, $result_name:ident;
22        }
23        $( $rest: tt )*
24    ) => {
25        impl_error_chain_processed! {
26            types {
27                $error_name, $error_kind_name,
28                $result_ext_name;
29            }
30            $( $rest )*
31        }
32        /// Convenient wrapper around `std::Result`.
33        #[allow(unused)]
34        pub type $result_name<T> = ::std::result::Result<T, $error_name>;
35    };
36    // Without `Result` wrapper.
37    (
38        types {
39            $error_name:ident, $error_kind_name:ident,
40            $result_ext_name:ident;
41        }
42
43        links {
44            $( $link_variant:ident ( $link_error_path:path, $link_kind_path:path )
45               $( #[$meta_links:meta] )*; ) *
46        }
47
48        foreign_links {
49            $( $foreign_link_variant:ident ( $foreign_link_error_path:path )
50               $( #[$meta_foreign_links:meta] )*; )*
51        }
52
53        errors {
54            $( $error_chunks:tt ) *
55        }
56
57    ) => {
58        /// The Error type.
59        ///
60        /// This tuple struct is made of two elements:
61        ///
62        /// - an `ErrorKind` which is used to determine the type of the error.
63        /// - An internal `State`, not meant for direct use outside of `error_chain`
64        ///   internals, containing:
65        ///   - a backtrace, generated when the error is created.
66        ///   - an error chain, used for the implementation of `Error::cause()`.
67        #[derive(Debug)]
68        pub struct $error_name(
69            // The members must be `pub` for `links`.
70            /// The kind of the error.
71            pub $error_kind_name,
72            /// Contains the error chain and the backtrace.
73            #[doc(hidden)]
74            pub $crate::State,
75        );
76
77        impl $crate::ChainedError for $error_name {
78            type ErrorKind = $error_kind_name;
79
80            fn new(kind: $error_kind_name, state: $crate::State) -> $error_name {
81                $error_name(kind, state)
82            }
83
84            fn from_kind(kind: Self::ErrorKind) -> Self {
85                Self::from_kind(kind)
86            }
87
88            fn with_chain<E, K>(error: E, kind: K)
89                -> Self
90                where E: ::std::error::Error + Send + 'static,
91                      K: Into<Self::ErrorKind>
92            {
93                Self::with_chain(error, kind)
94            }
95
96            fn kind(&self) -> &Self::ErrorKind {
97                self.kind()
98            }
99
100            fn iter(&self) -> $crate::Iter {
101                $crate::Iter::new(Some(self))
102            }
103
104            fn chain_err<F, EK>(self, error: F) -> Self
105                where F: FnOnce() -> EK,
106                      EK: Into<$error_kind_name> {
107                self.chain_err(error)
108            }
109
110            fn backtrace(&self) -> Option<&$crate::Backtrace> {
111                self.backtrace()
112            }
113
114            impl_extract_backtrace!($error_name
115                                    $error_kind_name
116                                    $([$link_error_path, $(#[$meta_links])*])*);
117        }
118
119        #[allow(dead_code)]
120        impl $error_name {
121            /// Constructs an error from a kind, and generates a backtrace.
122            pub fn from_kind(kind: $error_kind_name) -> $error_name {
123                $error_name(
124                    kind,
125                    $crate::State::default(),
126                )
127            }
128
129            /// Constructs a chained error from another error and a kind, and generates a backtrace.
130            pub fn with_chain<E, K>(error: E, kind: K)
131                -> $error_name
132                where E: ::std::error::Error + Send + 'static,
133                      K: Into<$error_kind_name>
134            {
135                $error_name::with_boxed_chain(Box::new(error), kind)
136            }
137
138            /// Construct a chained error from another boxed error and a kind, and generates a backtrace
139            pub fn with_boxed_chain<K>(error: Box<::std::error::Error + Send>, kind: K)
140                -> $error_name
141                where K: Into<$error_kind_name>
142            {
143                $error_name(
144                    kind.into(),
145                    $crate::State::new::<$error_name>(error, ),
146                )
147            }
148
149            /// Returns the kind of the error.
150            pub fn kind(&self) -> &$error_kind_name {
151                &self.0
152            }
153
154            /// Iterates over the error chain.
155            pub fn iter(&self) -> $crate::Iter {
156                $crate::ChainedError::iter(self)
157            }
158
159            /// Returns the backtrace associated with this error.
160            pub fn backtrace(&self) -> Option<&$crate::Backtrace> {
161                self.1.backtrace()
162            }
163
164            /// Extends the error chain with a new entry.
165            pub fn chain_err<F, EK>(self, error: F) -> $error_name
166                where F: FnOnce() -> EK, EK: Into<$error_kind_name> {
167                $error_name::with_chain(self, Self::from_kind(error().into()))
168            }
169
170            /// A short description of the error.
171            /// This method is identical to [`Error::description()`](https://doc.rust-lang.org/nightly/std/error/trait.Error.html#tymethod.description)
172            pub fn description(&self) -> &str {
173                self.0.description()
174            }
175        }
176
177        impl ::std::error::Error for $error_name {
178            fn description(&self) -> &str {
179                self.description()
180            }
181
182            #[allow(unknown_lints, renamed_and_removed_lints, unused_doc_comment, unused_doc_comments)]
183            fn cause(&self) -> Option<&::std::error::Error> {
184                match self.1.next_error {
185                    Some(ref c) => Some(&**c),
186                    None => {
187                        match self.0 {
188                            $(
189                                $(#[$meta_foreign_links])*
190                                $error_kind_name::$foreign_link_variant(ref foreign_err) => {
191                                    foreign_err.cause()
192                                }
193                            ) *
194                            _ => None
195                        }
196                    }
197                }
198            }
199        }
200
201        impl ::std::fmt::Display for $error_name {
202            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
203                ::std::fmt::Display::fmt(&self.0, f)
204            }
205        }
206
207        $(
208            $(#[$meta_links])*
209            impl From<$link_error_path> for $error_name {
210                fn from(e: $link_error_path) -> Self {
211                    $error_name(
212                        $error_kind_name::$link_variant(e.0),
213                        e.1,
214                    )
215                }
216            }
217        ) *
218
219        $(
220            $(#[$meta_foreign_links])*
221            impl From<$foreign_link_error_path> for $error_name {
222                fn from(e: $foreign_link_error_path) -> Self {
223                    $error_name::from_kind(
224                        $error_kind_name::$foreign_link_variant(e)
225                    )
226                }
227            }
228        ) *
229
230        impl From<$error_kind_name> for $error_name {
231            fn from(e: $error_kind_name) -> Self {
232                $error_name::from_kind(e)
233            }
234        }
235
236        impl<'a> From<&'a str> for $error_name {
237            fn from(s: &'a str) -> Self {
238                $error_name::from_kind(s.into())
239            }
240        }
241
242        impl From<String> for $error_name {
243            fn from(s: String) -> Self {
244                $error_name::from_kind(s.into())
245            }
246        }
247
248
249        // The ErrorKind type
250        // --------------
251
252        impl_error_chain_kind! {
253            /// The kind of an error.
254            #[derive(Debug)]
255            pub enum $error_kind_name {
256
257                /// A convenient variant for String.
258                Msg(s: String) {
259                    description(&s)
260                    display("{}", s)
261                }
262
263                $(
264                    $(#[$meta_links])*
265                    $link_variant(e: $link_kind_path) {
266                        description(e.description())
267                        display("{}", e)
268                    }
269                ) *
270
271                $(
272                    $(#[$meta_foreign_links])*
273                    $foreign_link_variant(err: $foreign_link_error_path) {
274                        description(::std::error::Error::description(err))
275                        display("{}", err)
276                    }
277                ) *
278
279                $($error_chunks)*
280            }
281        }
282
283        $(
284            $(#[$meta_links])*
285            impl From<$link_kind_path> for $error_kind_name {
286                fn from(e: $link_kind_path) -> Self {
287                    $error_kind_name::$link_variant(e)
288                }
289            }
290        ) *
291
292        impl<'a> From<&'a str> for $error_kind_name {
293            fn from(s: &'a str) -> Self {
294                $error_kind_name::Msg(s.to_string())
295            }
296        }
297
298        impl From<String> for $error_kind_name {
299            fn from(s: String) -> Self {
300                $error_kind_name::Msg(s)
301            }
302        }
303
304        impl From<$error_name> for $error_kind_name {
305            fn from(e: $error_name) -> Self {
306                e.0
307            }
308        }
309
310        // The ResultExt trait defines the `chain_err` method.
311
312        /// Additional methods for `Result`, for easy interaction with this crate.
313        pub trait $result_ext_name<T> {
314            /// If the `Result` is an `Err` then `chain_err` evaluates the closure,
315            /// which returns *some type that can be converted to `ErrorKind`*, boxes
316            /// the original error to store as the cause, then returns a new error
317            /// containing the original error.
318            fn chain_err<F, EK>(self, callback: F) -> ::std::result::Result<T, $error_name>
319                where F: FnOnce() -> EK,
320                      EK: Into<$error_kind_name>;
321        }
322
323        impl<T, E> $result_ext_name<T> for ::std::result::Result<T, E> where E: ::std::error::Error + Send + 'static {
324            fn chain_err<F, EK>(self, callback: F) -> ::std::result::Result<T, $error_name>
325                where F: FnOnce() -> EK,
326                      EK: Into<$error_kind_name> {
327                self.map_err(move |e| {
328                    let state = $crate::State::new::<$error_name>(Box::new(e), );
329                    $crate::ChainedError::new(callback().into(), state)
330                })
331            }
332        }
333
334        impl<T> $result_ext_name<T> for ::std::option::Option<T> {
335            fn chain_err<F, EK>(self, callback: F) -> ::std::result::Result<T, $error_name>
336                where F: FnOnce() -> EK,
337                      EK: Into<$error_kind_name> {
338                self.ok_or_else(move || {
339                    $crate::ChainedError::from_kind(callback().into())
340                })
341            }
342        }
343
344
345    };
346}
347
348/// Internal macro used for reordering of the fields.
349#[doc(hidden)]
350#[macro_export]
351macro_rules! error_chain_processing {
352    (
353        ({}, $b:tt, $c:tt, $d:tt)
354        types $content:tt
355        $( $tail:tt )*
356    ) => {
357        error_chain_processing! {
358            ($content, $b, $c, $d)
359            $($tail)*
360        }
361    };
362    (
363        ($a:tt, {}, $c:tt, $d:tt)
364        links $content:tt
365        $( $tail:tt )*
366    ) => {
367        error_chain_processing! {
368            ($a, $content, $c, $d)
369            $($tail)*
370        }
371    };
372    (
373        ($a:tt, $b:tt, {}, $d:tt)
374        foreign_links $content:tt
375        $( $tail:tt )*
376    ) => {
377        error_chain_processing! {
378            ($a, $b, $content, $d)
379            $($tail)*
380        }
381    };
382    (
383        ($a:tt, $b:tt, $c:tt, {})
384        errors $content:tt
385        $( $tail:tt )*
386    ) => {
387        error_chain_processing! {
388            ($a, $b, $c, $content)
389            $($tail)*
390        }
391    };
392    ( ($a:tt, $b:tt, $c:tt, $d:tt) ) => {
393        impl_error_chain_processed! {
394            types $a
395            links $b
396            foreign_links $c
397            errors $d
398        }
399    };
400}
401
402/// Macro for generating error types and traits. See crate level documentation for details.
403#[macro_export]
404macro_rules! error_chain {
405    ( $( $block_name:ident { $( $block_content:tt )* } )* ) => {
406        error_chain_processing! {
407            ({}, {}, {}, {})
408            $($block_name { $( $block_content )* })*
409        }
410    };
411}
412
413/// Macro used to manage the `backtrace` feature.
414///
415/// See
416/// https://www.reddit.com/r/rust/comments/57virt/hey_rustaceans_got_an_easy_question_ask_here/da5r4ti/?context=3
417/// for more details.
418#[macro_export]
419#[doc(hidden)]
420macro_rules! impl_extract_backtrace {
421    ($error_name: ident
422     $error_kind_name: ident
423     $([$link_error_path: path, $(#[$meta_links: meta])*])*) => {
424        #[allow(unknown_lints, renamed_and_removed_lints, unused_doc_comment, unused_doc_comments)]
425        fn extract_backtrace(e: &(::std::error::Error + Send + 'static))
426            -> Option<$crate::InternalBacktrace> {
427            if let Some(e) = e.downcast_ref::<$error_name>() {
428                return Some(e.1.backtrace.clone());
429            }
430            $(
431                $( #[$meta_links] )*
432                {
433                    if let Some(e) = e.downcast_ref::<$link_error_path>() {
434                        return Some(e.1.backtrace.clone());
435                    }
436                }
437            ) *
438            None
439        }
440    }
441}