ckb_error/
util.rs

1//! Error-related macros
2
3/// Compare two errors.
4///
5/// NOTE: Used for testing only!
6#[doc(hidden)]
7#[macro_export]
8macro_rules! assert_error_eq {
9    ($left:expr, $right:expr) => {
10        assert_eq!(
11            Into::<$crate::Error>::into($left).to_string(),
12            Into::<$crate::Error>::into($right).to_string(),
13        );
14    };
15    ($left:expr, $right:expr,) => {
16        $crate::assert_error_eq!($left, $right);
17    };
18    ($left:expr, $right:expr, $($arg:tt)+) => {
19        assert_eq!(
20            Into::<$crate::Error>::into($left).to_string(),
21            Into::<$crate::Error>::into($right).to_string(),
22            $($arg)+
23        );
24    }
25}
26
27/// A macro to implement conversion from source type to target type with an implicit error kind.
28///
29/// ## Examples
30///
31/// ```text
32/// impl_error_conversion_with_kind!(SourceType, error_kind, TargetType)
33/// ```
34///
35/// the expanded code:
36///
37/// ```text
38/// impl From<SourceType> for TargetType {
39///     fn from(source: SourceType) -> TargetType {
40///         TargetType {
41///             kind: error_kind,
42///             inner: source.into(),
43///         }
44///     }
45/// }
46/// ```
47#[doc(hidden)]
48#[macro_export]
49macro_rules! impl_error_conversion_with_kind {
50    ($source:ty, $kind:expr, $target:ty) => {
51        impl ::std::convert::From<$source> for $target {
52            fn from(error: $source) -> Self {
53                $kind.because(error)
54            }
55        }
56    };
57}
58
59/// A macro to implement conversion from source type to target type based on an implicit middle adaptor.
60///
61/// ## Examples
62///
63/// ```text
64/// impl_error_conversion_with_adaptor!(SourceType, AdaptorType, TargetType)
65/// ```
66///
67/// the expanded code:
68///
69/// ```text
70/// impl From<SourceType> for TargetType {
71///     fn from(source: SourceType) -> TargetType {
72///         let adaptor: AdaptorType = source.into();
73///         let target: TargetType = adaptor.into();
74///         target
75///     }
76/// }
77/// ```
78#[doc(hidden)]
79#[macro_export]
80macro_rules! impl_error_conversion_with_adaptor {
81    ($source:ty, $adaptor:ty, $target:ty) => {
82        impl ::std::convert::From<$source> for $target {
83            fn from(error: $source) -> Self {
84                ::std::convert::Into::<$adaptor>::into(error).into()
85            }
86        }
87    };
88}
89
90#[doc(hidden)]
91#[macro_export]
92macro_rules! def_error_base_on_kind {
93    ($error:ident, $error_kind:ty, $comment_error:expr, $comment_because:expr, $comment_simple:expr) => {
94        #[doc = $comment_error]
95        #[derive(Error, Debug, Clone)]
96        pub struct $error {
97            kind: $error_kind,
98            inner: $crate::AnyError,
99        }
100
101        impl ::std::fmt::Display for $error {
102            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
103                if let Some(err) = self.cause() {
104                    if f.alternate() {
105                        write!(f, "{}: {}", self.kind(), err)
106                    } else {
107                        write!(f, "{}({})", self.kind(), err)
108                    }
109                } else {
110                    write!(f, "{}", self.kind())
111                }
112            }
113        }
114
115        impl ::std::convert::From<$error_kind> for $error {
116            fn from(kind: $error_kind) -> Self {
117                kind.because($crate::SilentError)
118            }
119        }
120
121        impl $error_kind {
122            #[doc = $comment_because]
123            pub fn because<E>(self, reason: E) -> $error
124            where
125                E: ::std::error::Error + Send + Sync + 'static,
126            {
127                $error {
128                    kind: self,
129                    inner: reason.into(),
130                }
131            }
132
133            #[doc = $comment_simple]
134            pub fn other<T>(self, reason: T) -> $error
135            where
136                T: ::std::fmt::Display,
137            {
138                $error {
139                    kind: self,
140                    inner: $crate::OtherError::new(reason.to_string()).into(),
141                }
142            }
143        }
144
145        impl $error {
146            /// Returns the general category of this error.
147            pub fn kind(&self) -> $error_kind {
148                self.kind
149            }
150
151            /// Downcast this error object by reference.
152            pub fn downcast_ref<E>(&self) -> Option<&E>
153            where
154                E: ::std::fmt::Display + ::std::fmt::Debug + Send + Sync + 'static,
155            {
156                self.inner.downcast_ref::<E>()
157            }
158
159            /// The lowest level cause of this error — this error's cause's cause's cause etc.
160            pub fn root_cause(&self) -> &(dyn ::std::error::Error + 'static) {
161                self.inner.root_cause()
162            }
163
164            /// The lower-level source of this error, if any.
165            pub fn cause(&self) -> Option<&(dyn ::std::error::Error + 'static)> {
166                self.inner.chain().next()
167            }
168        }
169    };
170    ($error:ident, $error_kind:ty, $comment_error:expr) => {
171        def_error_base_on_kind!(
172            $error,
173            $error_kind,
174            $comment_error,
175            concat!(
176                "Creates `",
177                stringify!($error),
178                "` base on `",
179                stringify!($error_kind),
180                "` with an error as the reason."
181            ),
182            concat!(
183                "Creates `",
184                stringify!($error),
185                "` base on `",
186                stringify!($error_kind),
187                "` with a simple string as the reason."
188            )
189        );
190    };
191    ($error:ident, $error_kind:ty) => {
192        def_error_base_on_kind!($error, $error_kind, "/// TODO(doc): @keroro520");
193    };
194}