1 // Tagged dispatch mechanism for resolving the behavior of `anyhow!($expr)`.
2 //
3 // When anyhow! is given a single expr argument to turn into anyhow::Error, we
4 // want the resulting Error to pick up the input's implementation of source()
5 // and backtrace() if it has a std::error::Error impl, otherwise require nothing
6 // more than Display and Debug.
7 //
8 // Expressed in terms of specialization, we want something like:
9 //
10 //     trait AnyhowNew {
11 //         fn new(self) -> Error;
12 //     }
13 //
14 //     impl<T> AnyhowNew for T
15 //     where
16 //         T: Display + Debug + Send + Sync + 'static,
17 //     {
18 //         default fn new(self) -> Error {
19 //             /* no std error impl */
20 //         }
21 //     }
22 //
23 //     impl<T> AnyhowNew for T
24 //     where
25 //         T: std::error::Error + Send + Sync + 'static,
26 //     {
27 //         fn new(self) -> Error {
28 //             /* use std error's source() and backtrace() */
29 //         }
30 //     }
31 //
32 // Since specialization is not stable yet, instead we rely on autoref behavior
33 // of method resolution to perform tagged dispatch. Here we have two traits
34 // AdhocKind and TraitKind that both have an anyhow_kind() method. AdhocKind is
35 // implemented whether or not the caller's type has a std error impl, while
36 // TraitKind is implemented only when a std error impl does exist. The ambiguity
37 // is resolved by AdhocKind requiring an extra autoref so that it has lower
38 // precedence.
39 //
40 // The anyhow! macro will set up the call in this form:
41 //
42 //     #[allow(unused_imports)]
43 //     use $crate::private::{AdhocKind, TraitKind};
44 //     let error = $msg;
45 //     (&error).anyhow_kind().new(error)
46 
47 use crate::Error;
48 use core::fmt::{Debug, Display};
49 
50 #[cfg(feature = "std")]
51 use crate::StdError;
52 
53 pub struct Adhoc;
54 
55 pub trait AdhocKind: Sized {
56     #[inline]
anyhow_kind(&self) -> Adhoc57     fn anyhow_kind(&self) -> Adhoc {
58         Adhoc
59     }
60 }
61 
62 impl<T> AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {}
63 
64 impl Adhoc {
new<M>(self, message: M) -> Error where M: Display + Debug + Send + Sync + 'static,65     pub fn new<M>(self, message: M) -> Error
66     where
67         M: Display + Debug + Send + Sync + 'static,
68     {
69         Error::from_adhoc(message, backtrace!())
70     }
71 }
72 
73 pub struct Trait;
74 
75 pub trait TraitKind: Sized {
76     #[inline]
anyhow_kind(&self) -> Trait77     fn anyhow_kind(&self) -> Trait {
78         Trait
79     }
80 }
81 
82 impl<E> TraitKind for E where E: Into<Error> {}
83 
84 impl Trait {
new<E>(self, error: E) -> Error where E: Into<Error>,85     pub fn new<E>(self, error: E) -> Error
86     where
87         E: Into<Error>,
88     {
89         error.into()
90     }
91 }
92 
93 #[cfg(feature = "std")]
94 pub struct Boxed;
95 
96 #[cfg(feature = "std")]
97 pub trait BoxedKind: Sized {
98     #[inline]
anyhow_kind(&self) -> Boxed99     fn anyhow_kind(&self) -> Boxed {
100         Boxed
101     }
102 }
103 
104 #[cfg(feature = "std")]
105 impl BoxedKind for Box<dyn StdError + Send + Sync> {}
106 
107 #[cfg(feature = "std")]
108 impl Boxed {
new(self, error: Box<dyn StdError + Send + Sync>) -> Error109     pub fn new(self, error: Box<dyn StdError + Send + Sync>) -> Error {
110         let backtrace = backtrace_if_absent!(error);
111         Error::from_boxed(error, backtrace)
112     }
113 }
114