thiserror_context/composition.rs
1/// Allows for converting from a child error type to a parent
2/// error type while preserving any context on the child error.
3///
4/// This is intended to be used when:
5/// 1. Both Source and Target are context enriched thiserror enums
6/// 2. Source is a variant of Target's inner error
7///
8/// ** Example **
9/// ```
10/// use thiserror::Error;
11/// use thiserror_context::{Context, impl_context, impl_from_carry_context};
12///
13/// // Some inner error type
14/// #[derive(Debug, Error)]
15/// pub enum InnerError {
16/// #[error("dummy")]
17/// Dummy,
18/// }
19/// impl_context!(Inner(InnerError));
20///
21/// // And some outer error type, which contains
22/// // a variant of the inner error type
23/// #[derive(Debug, Error)]
24/// pub enum OuterError {
25/// #[error("inner error")]
26/// // we explicitly do _not_ use #[from] here, instead
27/// // opting to use the macro to create the conversion
28/// // and handle the context propagation.
29/// Inner(Inner),
30/// }
31/// impl_context!(Outer(OuterError));
32///
33/// // Then we use the macro to implement the conversion
34/// // from Inner to Outer
35/// impl_from_carry_context!(Inner, Outer, OuterError::Inner);
36///
37/// fn inner_call() -> Result<(), Inner> {
38/// Err(InnerError::Dummy)
39/// .context("context on inner")
40/// }
41///
42/// fn outer_call() -> Result<(), Outer> {
43/// inner_call().context("context on outer")
44/// }
45///
46/// let r = outer_call();
47/// assert!(r.is_err());
48/// let e = r.unwrap_err();
49/// assert_eq!(format!("{:?}",e), r#"Inner(Dummy)
50///
51/// Caused by:
52/// 0: context on outer
53/// 1: context on inner
54/// "#);
55/// ```
56#[macro_export]
57macro_rules! impl_from_carry_context {
58 ($source: ident, $target: ident, $variant: path) => {
59 impl From<$source> for $target {
60 fn from(mut value: $source) -> Self {
61 let mut contexts = vec![];
62
63 let inner = loop {
64 match value {
65 $source::Base(x) => break x,
66 $source::Context { context, error } => {
67 contexts.push(context);
68 value = *error;
69 }
70 }
71 };
72 let inner = $source::Base(inner);
73
74 let mut x = $target::Base($variant(inner));
75
76 for ctx in contexts.into_iter().rev() {
77 x = $target::Context {
78 context: ctx,
79 error: Box::new(x),
80 };
81 }
82
83 x
84 }
85 }
86 };
87}
88
89pub use impl_from_carry_context;