1#[macro_export]
34macro_rules! err_downcast_ref {
35 ( $err:expr ) => {
37 { let _ = $err; None }
38 };
39 ( $err:expr, $($v:ident : $ty:ty => $action:expr),* , ) => {
41 err_downcast_ref!($err, $($v : $ty => $action),*)
42 };
43 ( $err:expr, $v:ident : $ty:ty => $action:expr $(, $rv:ident : $rty:ty => $raction:expr)* ) => {{
47 match $err.downcast_ref::<$ty>() {
48 Some($v) => Some($action),
49 None => err_downcast_ref!($err $(, $rv : $rty => $raction)*),
50 }
51 }};
52}
53
54#[macro_export]
78macro_rules! err_downcast {
79 ( $err:expr ) => {
81 Err($err)
82 };
83 ( $err:expr, $($v:ident : $ty:ty => $action:expr),* , ) => {
85 err_downcast!($err, $($v : $ty => $action),*)
86 };
87 ( $err:expr, $v:ident : $ty:ty => $action:expr $(, $rv:ident : $rty:ty => $raction:expr)* ) => {{
91 match $err.downcast::<$ty>() {
92 Ok($v) => Ok($action),
93 Err(other) => err_downcast!(other $(, $rv : $rty => $raction)*),
94 }
95 }};
96}
97
98#[allow(clippy::disallowed_names)]
99#[cfg(test)]
100mod test {
101 use anyhow::Error;
102 use thiserror::Error;
103
104 #[derive(Error, Debug)]
105 #[error("Foo badness")]
106 struct Foo;
107 #[derive(Error, Debug)]
108 #[error("Bar badness")]
109 struct Bar;
110 #[derive(Error, Debug)]
111 #[error("Blat badness")]
112 struct Blat;
113 #[derive(Error, Debug)]
114 #[error("Outer badness")]
115 struct Outer;
116
117 #[test]
118 fn downcast_ref_syntax() {
119 let blat = Error::from(Blat);
120
121 let _ = err_downcast_ref! {
123 blat,
124 v: Foo => v.to_string(),
125 };
126
127 let _ = err_downcast_ref! {
129 blat,
130 v: Foo => v.to_string()
131 };
132
133 let _ = err_downcast_ref! {
135 blat,
136 v: Foo => v.to_string(),
137 v: Blat => v.to_string(),
138 };
139
140 let _ = err_downcast_ref! {
142 blat,
143 v: Foo => v.to_string(),
144 v: Blat => v.to_string()
145 };
146 }
147
148 #[test]
149 fn downcast_ref_basic() {
150 let blat = Error::from(Blat);
151
152 let msg = err_downcast_ref! {
153 blat,
154 foo: Foo => foo.to_string(),
155 bar: Bar => bar.to_string(),
156 blat: Blat => blat.to_string(),
157 outer: Outer => outer.to_string(),
158 };
159
160 assert_eq!(msg.unwrap(), "Blat badness".to_string());
161 }
162
163 #[allow(clippy::cognitive_complexity)]
164 #[test]
165 fn downcast_ref_context() {
166 let foo = Error::from(Foo);
167 let outer = foo.context(Outer);
168
169 let msg1 = err_downcast_ref! {
170 outer,
171 foo: Foo => foo.to_string(), bar: Bar => bar.to_string(),
173 blat: Blat => blat.to_string(),
174 outer: Outer => outer.to_string(),
175 };
176 let msg2 = err_downcast_ref! {
177 outer,
178 blat: Blat => blat.to_string(),
179 outer: Outer => outer.to_string(), foo: Foo => foo.to_string(),
181 bar: Bar => bar.to_string(),
182 };
183
184 assert_eq!(msg1.unwrap(), "Foo badness".to_string());
185 assert_eq!(msg2.unwrap(), "Outer badness".to_string());
186 }
187
188 #[test]
189 fn downcast_ref_miss() {
190 let blat = Error::from(Blat);
191
192 let msg = err_downcast_ref! {
193 blat,
194 v: Foo => { let _: &Foo = v; v.to_string() },
195 v: Bar => { let _: &Bar = v; v.to_string() },
196 };
197
198 assert!(msg.is_none());
199 assert!(blat.downcast_ref::<Blat>().is_some());
200 }
201
202 #[test]
203 fn downcast_syntax() {
204 let blat = Error::from(Blat);
206 let _ = err_downcast! {
207 blat,
208 v: Foo => v.to_string(),
209 };
210
211 let blat = Error::from(Blat);
213 let _ = err_downcast! {
214 blat,
215 v: Foo => v.to_string()
216 };
217
218 let blat = Error::from(Blat);
220 let _ = err_downcast! {
221 blat,
222 v: Foo => v.to_string(),
223 v: Blat => v.to_string(),
224 };
225
226 let blat = Error::from(Blat);
228 let _ = err_downcast! {
229 blat,
230 v: Foo => v.to_string(),
231 v: Blat => v.to_string()
232 };
233 }
234
235 #[test]
236 fn downcast_basic() {
237 let blat = Error::from(Blat);
238
239 let msg = err_downcast! {
240 blat,
241 foo: Foo => foo.to_string(),
242 bar: Bar => bar.to_string(),
243 blat: Blat => blat.to_string(),
244 outer: Outer => outer.to_string(),
245 };
246
247 assert_eq!(msg.unwrap(), "Blat badness".to_string());
248 }
249
250 #[test]
251 fn downcast_context() {
252 let foo = Error::from(Foo);
253 let outer = foo.context(Outer);
254
255 let msg = err_downcast! {
256 outer,
257 v: Foo => { let _: Foo = v; v.to_string() },
258 v: Bar => { let _: Bar = v; v.to_string() },
259 v: Blat => { let _: Blat = v; v.to_string() },
260 v: Outer => { let _: Outer = v; v.to_string() },
261 };
262
263 assert_eq!(msg.unwrap(), "Foo badness".to_string());
264 }
265
266 #[test]
267 fn downcast_miss() {
268 let blat = Error::from(Blat);
269
270 let msg = err_downcast! {
271 blat,
272 foo: Foo => foo.to_string(),
273 bar: Bar => bar.to_string(),
274 outer: Outer => outer.to_string(),
275 };
276
277 assert!(msg.is_err());
278 assert!(msg.unwrap_err().downcast::<Blat>().is_ok());
279 }
280}