1#[cfg(feature = "tracing")]
2use std::fmt::Display;
3
4#[doc(hidden)] pub use eyre;
6
7pub mod prelude {
8 #[cfg(feature = "tracing")]
9 pub use crate::ResultExt;
10}
11
12#[cfg(feature = "tracing")]
15pub trait ResultExt {
16 fn log_err(self, msg: impl Display) -> Self;
20
21 fn log_err_with<F>(self, f: F) -> Self
23 where
24 F: FnOnce() -> String;
25
26 fn log_warn(self, msg: impl Display) -> Self;
30
31 fn log_warn_with<F>(self, f: F) -> Self
33 where
34 F: FnOnce() -> String;
35
36 fn log_info(self, msg: impl Display) -> Self;
40
41 fn log_info_with<F>(self, f: F) -> Self
43 where
44 F: FnOnce() -> String;
45
46 fn log_debug(self, msg: impl Display) -> Self;
50
51 fn log_debug_with<F>(self, f: F) -> Self
53 where
54 F: FnOnce() -> String;
55
56 fn log_trace(self, msg: impl Display) -> Self;
60
61 fn log_trace_with<F>(self, f: F) -> Self
63 where
64 F: FnOnce() -> String;
65}
66
67#[cfg(feature = "tracing")]
68impl<T, E> ResultExt for Result<T, E>
69where
70 E: std::fmt::Debug,
71{
72 fn log_err(self, msg: impl Display) -> Self {
73 self.inspect_err(|error| tracing::error!(?error, "{msg}"))
74 }
75
76 fn log_err_with<F>(self, msg: F) -> Self
77 where
78 F: FnOnce() -> String,
79 {
80 self.inspect_err(|error| tracing::error!(?error, "{}", msg()))
81 }
82
83 fn log_warn(self, msg: impl Display) -> Self {
84 self.inspect_err(|error| tracing::warn!(?error, "{msg}"))
85 }
86
87 fn log_warn_with<F>(self, msg: F) -> Self
88 where
89 F: FnOnce() -> String,
90 {
91 self.inspect_err(|error| tracing::warn!(?error, "{}", msg()))
92 }
93
94 fn log_info(self, msg: impl Display) -> Self {
95 self.inspect_err(|error| tracing::info!(?error, "{msg}"))
96 }
97
98 fn log_info_with<F>(self, msg: F) -> Self
99 where
100 F: FnOnce() -> String,
101 {
102 self.inspect_err(|error| tracing::info!(?error, "{}", msg()))
103 }
104
105 fn log_debug(self, msg: impl Display) -> Self {
106 self.inspect_err(|error| tracing::debug!(?error, "{msg}"))
107 }
108
109 fn log_debug_with<F>(self, msg: F) -> Self
110 where
111 F: FnOnce() -> String,
112 {
113 self.inspect_err(|error| tracing::debug!(?error, "{}", msg()))
114 }
115
116 fn log_trace(self, msg: impl Display) -> Self {
117 self.inspect_err(|error| tracing::trace!(?error, "{msg}"))
118 }
119
120 fn log_trace_with<F>(self, msg: F) -> Self
121 where
122 F: FnOnce() -> String,
123 {
124 self.inspect_err(|error| tracing::trace!(?error, "{}", msg()))
125 }
126}
127
128#[macro_export]
152macro_rules! match_err {
153 (
154 $expr:expr
155 $(, type<$ty:ty>($pat:pat) => $handler:expr)*
156 $(, else($default_pat:pat) => $default_handler:expr)?
157 $(,)?
158 ) => {
159 'result: {
160 let expr: $crate::eyre::Report = $expr;
161 $(
162 #[allow(unused_variables)] if let Some($pat) = expr.downcast_ref::<$ty>() {
164 let Ok($pat) = expr.downcast::<$ty>() else {
165 unreachable!("downcast_ref succeeded but downcast failed");
167 };
168 let res = $handler;
169 break 'result res;
170 }
171 )*
172 $(
173 let $default_pat = expr;
174 $default_handler
175 )?
176 }
177 };
178}
179
180#[macro_export]
221macro_rules! match_res {
222 (
223 $expr:expr
224 $(, Ok($ok:pat) => $ok_handler:expr)*
225 $(, Err(type<$err_ty:ty>($err:pat)) => $err_handler:expr)*
226 $(,)?
227 ) => {
228 $crate::match_res!($expr
229 $(, Ok($ok) => $ok_handler)*
230 $(, Err(type<$err_ty>($err)) => $err_handler)*
231 , Err(else(err)) => { return Err(err) }
232 )
233 };
234
235 (
236 $expr:expr
237 $(, Ok($ok:pat) => $ok_handler:expr)*
238 $(, Err(type<$err_ty:ty>($err:pat)) => $err_handler:expr)*
239 , Err(else($default_err:pat)) => $default_handler:expr
240 $(,)?
241 ) => {
242 match $expr {
243 $(
244 Ok($ok) => $ok_handler,
245 )*
246 Err(err) => $crate::match_err!(err,
247 $(
248 type<$err_ty>($err) => $err_handler,
249 )*
250 else($default_err) => $default_handler,
251 ),
252 }
253 };
254}
255
256#[cfg(test)]
257mod tests {
258 use eyre::Context as _;
259
260 #[derive(Debug, thiserror::Error)]
261 #[error("Foo")]
262 struct Foo;
263
264 #[derive(Debug, thiserror::Error)]
265 #[error("Bar")]
266 struct Bar(&'static str);
267
268 #[derive(Debug, thiserror::Error)]
269 #[error("Quux")]
270 struct Quux;
271
272 fn use_match_err_to_make_a_value<E: Into<eyre::Report>>(err: E) -> String {
273 let err: eyre::Report = err.into();
274 match_err!(err,
275 type<Foo>(_) => "foo".to_string(),
276 type<Bar>(Bar("foo")) => "bar/foo".to_string(),
277 type<Bar>(Bar(s)) => format!("bar({s})"),
278 else(_) => "other".to_string(),
279 )
280 }
281
282 #[test]
283 fn match_err_to_make_a_value() {
284 assert_eq!(use_match_err_to_make_a_value(Foo), "foo");
286 assert_eq!(use_match_err_to_make_a_value(Bar("baz")), "bar(baz)");
287 assert_eq!(use_match_err_to_make_a_value(Bar("foo")), "bar/foo");
288
289 assert_eq!(use_match_err_to_make_a_value(Quux), "other");
291 assert_eq!(use_match_err_to_make_a_value(eyre::eyre!("baz")), "other");
292
293 assert_eq!(
295 use_match_err_to_make_a_value(eyre::Report::from(Foo).wrap_err("with some context")),
296 "foo"
297 );
298
299 assert_eq!(
303 use_match_err_to_make_a_value(eyre::Report::from(Foo).wrap_err(Bar("baz"))),
304 "foo"
305 );
306 assert_eq!(
307 use_match_err_to_make_a_value(eyre::Report::from(Bar("baz")).wrap_err(Foo)),
308 "foo"
309 );
310
311 assert_eq!(
314 use_match_err_to_make_a_value(eyre::Report::from(Quux).wrap_err(Foo)),
315 "foo"
316 );
317 assert_eq!(
318 use_match_err_to_make_a_value(eyre::Report::from(Quux).wrap_err(Foo).wrap_err(Quux)),
319 "foo"
320 );
321 }
322
323 fn use_match_err_to_run_code<E: Into<eyre::Report>>(err: E) -> String {
324 let err: eyre::Report = err.into();
325 let mut res = "other".to_string();
326 match_err!(err,
327 type<Foo>(_) => {
328 res = "foo".to_string();
329 },
330 type<Bar>(Bar("foo")) => {
331 res = "bar/foo".to_string();
332 },
333 type<Bar>(Bar(s)) => {
334 res = format!("bar({s})");
335 },
336 );
338 res
339 }
340
341 #[test]
342 fn match_err_to_run_code() {
343 assert_eq!(use_match_err_to_run_code(Foo), "foo");
345 assert_eq!(use_match_err_to_run_code(Bar("baz")), "bar(baz)");
346 assert_eq!(use_match_err_to_run_code(Bar("foo")), "bar/foo");
347
348 assert_eq!(use_match_err_to_run_code(Quux), "other");
350 assert_eq!(use_match_err_to_run_code(eyre::eyre!("baz")), "other");
351
352 assert_eq!(
354 use_match_err_to_run_code(eyre::Report::from(Foo).wrap_err("with some context")),
355 "foo"
356 );
357
358 assert_eq!(
362 use_match_err_to_run_code(eyre::Report::from(Foo).wrap_err(Bar("baz"))),
363 "foo"
364 );
365 assert_eq!(
366 use_match_err_to_run_code(eyre::Report::from(Bar("baz")).wrap_err(Foo)),
367 "foo"
368 );
369
370 assert_eq!(
373 use_match_err_to_run_code(eyre::Report::from(Quux).wrap_err(Foo)),
374 "foo"
375 );
376 assert_eq!(
377 use_match_err_to_run_code(eyre::Report::from(Quux).wrap_err(Foo).wrap_err(Quux)),
378 "foo"
379 );
380 }
381
382 fn use_match_res_with_default(res: eyre::Result<u32>) -> String {
383 match_res!(res,
384 Ok(42) => "the answer".to_string(),
385 Ok(n) => format!("ok({n})"),
386 Err(type<Bar>(Bar("foo"))) => "bar/foo".to_string(),
387 Err(type<Bar>(b)) => format!("bar({})", b.0),
388 Err(else(err)) => format!("other({err})"),
389 )
390 }
391
392 #[test]
393 fn match_err_with_default() {
394 assert_eq!(use_match_res_with_default(Ok(42)), "the answer");
395 assert_eq!(use_match_res_with_default(Ok(7)), "ok(7)");
396 assert_eq!(
397 use_match_res_with_default(Err(Bar("foo").into())),
398 "bar/foo"
399 );
400 assert_eq!(
401 use_match_res_with_default(Err(Bar("baz").into())),
402 "bar(baz)"
403 );
404 assert_eq!(use_match_res_with_default(Err(Foo.into())), "other(Foo)");
405
406 assert_eq!(
407 use_match_res_with_default(Err(eyre::Report::from(Bar("baz"))).wrap_err(Quux)),
408 "bar(baz)"
409 );
410 }
411
412 fn use_match_res_without_default(res: eyre::Result<u32>) -> eyre::Result<String> {
413 match_res!(res,
414 Ok(42) => Ok("the answer".to_string()),
415 Ok(n) => Ok(format!("ok({n})")),
416 Err(type<Bar>(Bar("foo"))) => Ok("bar/foo".to_string()),
417 Err(type<Bar>(b)) => Ok(format!("bar({})", b.0)),
418 )
420 }
421
422 #[test]
423 fn match_err_without_default() {
424 assert_eq!(use_match_res_without_default(Ok(42)).unwrap(), "the answer");
425 assert_eq!(use_match_res_without_default(Ok(7)).unwrap(), "ok(7)");
426 assert_eq!(
427 use_match_res_without_default(Err(Bar("foo").into())).unwrap(),
428 "bar/foo"
429 );
430 assert_eq!(
431 use_match_res_without_default(Err(Bar("baz").into())).unwrap(),
432 "bar(baz)"
433 );
434 assert!(use_match_res_without_default(Err(Foo.into())).is_err());
435
436 assert_eq!(
437 use_match_res_without_default(Err(eyre::Report::from(Bar("baz"))).wrap_err(Quux))
438 .unwrap(),
439 "bar(baz)"
440 );
441 }
442}