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 #[doc(hidden)]
56 pub trait AdhocKind: Sized {
57     #[inline]
anyhow_kind(&self) -> Adhoc58     fn anyhow_kind(&self) -> Adhoc {
59         Adhoc
60     }
61 }
62 
63 impl<T> AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {}
64 
65 impl Adhoc {
66     #[cold]
new<M>(self, message: M) -> Error where M: Display + Debug + Send + Sync + 'static,67     pub fn new<M>(self, message: M) -> Error
68     where
69         M: Display + Debug + Send + Sync + 'static,
70     {
71         Error::from_adhoc(message, backtrace!())
72     }
73 }
74 
75 pub struct Trait;
76 
77 #[doc(hidden)]
78 pub trait TraitKind: Sized {
79     #[inline]
anyhow_kind(&self) -> Trait80     fn anyhow_kind(&self) -> Trait {
81         Trait
82     }
83 }
84 
85 impl<E> TraitKind for E where E: Into<Error> {}
86 
87 impl Trait {
88     #[cold]
new<E>(self, error: E) -> Error where E: Into<Error>,89     pub fn new<E>(self, error: E) -> Error
90     where
91         E: Into<Error>,
92     {
93         error.into()
94     }
95 }
96 
97 #[cfg(feature = "std")]
98 pub struct Boxed;
99 
100 #[cfg(feature = "std")]
101 #[doc(hidden)]
102 pub trait BoxedKind: Sized {
103     #[inline]
anyhow_kind(&self) -> Boxed104     fn anyhow_kind(&self) -> Boxed {
105         Boxed
106     }
107 }
108 
109 #[cfg(feature = "std")]
110 impl BoxedKind for Box<dyn StdError + Send + Sync> {}
111 
112 #[cfg(feature = "std")]
113 impl Boxed {
114     #[cold]
new(self, error: Box<dyn StdError + Send + Sync>) -> Error115     pub fn new(self, error: Box<dyn StdError + Send + Sync>) -> Error {
116         let backtrace = backtrace_if_absent!(&*error);
117         Error::from_boxed(error, backtrace)
118     }
119 }
120