core_crypto/error/
recursive.rs

1/// These errors wrap each of the module-specific errors in CoreCrypto.
2///
3/// The goal here is to reduce the need to redeclare each of these error
4/// types as an individual variant of a module-specific error type.
5#[derive(Debug)]
6pub enum RecursiveError {
7    /// Wrap a [crate::Error] for recursion.
8    Root {
9        /// What was happening in the caller
10        context: &'static str,
11        /// What happened
12        source: Box<crate::Error>,
13    },
14    /// Wrap a [crate::transaction_context::Error] for recursion.
15    TransactionContext {
16        /// What was happening in the caller
17        context: &'static str,
18        /// What happened
19        source: Box<crate::transaction_context::Error>,
20    },
21    /// Wrap a [crate::e2e_identity::Error] for recursion.
22    E2e {
23        /// What was happening in the caller
24        context: &'static str,
25        /// What happened
26        source: Box<crate::e2e_identity::Error>,
27    },
28    /// Wrap a [crate::mls::Error] for recursion.
29    Mls {
30        /// What was happening in the caller
31        context: &'static str,
32        /// What happened
33        source: Box<crate::mls::Error>,
34    },
35    /// Wrap a [crate::mls::client::Error] for recursion.
36    MlsClient {
37        /// What was happening in the caller
38        context: &'static str,
39        /// What happened
40        source: Box<crate::mls::session::Error>,
41    },
42    /// Wrap a [crate::mls::conversation::Error] for recursion.
43    MlsConversation {
44        /// What was happening in the caller
45        context: &'static str,
46        /// What happened
47        source: Box<crate::mls::conversation::Error>,
48    },
49    /// Wrap a [crate::mls::credential::Error] for recursion.
50    MlsCredential {
51        /// What was happening in the caller
52        context: &'static str,
53        /// What happened
54        source: Box<crate::mls::credential::Error>,
55    },
56    /// Wrap a [crate::test_utils::TestError] for recursion.
57    #[cfg(test)]
58    Test(Box<crate::test_utils::TestError>),
59}
60
61impl RecursiveError {
62    /// Convert a [crate::Error] into a [RecursiveError], with context
63    pub fn root<E: Into<crate::Error>>(context: &'static str) -> impl FnOnce(E) -> Self {
64        move |into_source| Self::Root {
65            context,
66            source: Box::new(into_source.into()),
67        }
68    }
69
70    /// Convert a [crate::transaction_context::Error] into a [RecursiveError], with context
71    pub fn transaction<E: Into<crate::transaction_context::Error>>(context: &'static str) -> impl FnOnce(E) -> Self {
72        move |into_source| Self::TransactionContext {
73            context,
74            source: Box::new(into_source.into()),
75        }
76    }
77
78    /// Convert a [crate::e2e_identity::Error] into a [RecursiveError], with context
79    pub fn e2e_identity<E: Into<crate::e2e_identity::Error>>(context: &'static str) -> impl FnOnce(E) -> Self {
80        move |into_source| Self::E2e {
81            context,
82            source: Box::new(into_source.into()),
83        }
84    }
85
86    /// Convert a [crate::mls::Error] into a [RecursiveError], with context
87    pub fn mls<E: Into<crate::mls::Error>>(context: &'static str) -> impl FnOnce(E) -> Self {
88        move |into_source| Self::Mls {
89            context,
90            source: Box::new(into_source.into()),
91        }
92    }
93
94    /// Convert a [crate::mls::client::Error] into a [RecursiveError], with context
95    pub fn mls_client<E: Into<crate::mls::session::Error>>(context: &'static str) -> impl FnOnce(E) -> Self {
96        move |into_source| Self::MlsClient {
97            context,
98            source: Box::new(into_source.into()),
99        }
100    }
101
102    /// Convert a [crate::mls::conversation::Error] into a [RecursiveError], with context
103    pub fn mls_conversation<E: Into<crate::mls::conversation::Error>>(context: &'static str) -> impl FnOnce(E) -> Self {
104        move |into_source| Self::MlsConversation {
105            context,
106            source: Box::new(into_source.into()),
107        }
108    }
109
110    /// Convert a [crate::mls::credential::Error] into a [RecursiveError], with context
111    pub fn mls_credential<E: Into<crate::mls::credential::Error>>(context: &'static str) -> impl FnOnce(E) -> Self {
112        move |into_source| Self::MlsCredential {
113            context,
114            source: Box::new(into_source.into()),
115        }
116    }
117
118    #[cfg(test)]
119    pub(crate) fn test<E: Into<crate::test_utils::TestError>>() -> impl FnOnce(E) -> Self {
120        move |into_source| Self::Test(Box::new(into_source.into()))
121    }
122}
123
124impl std::fmt::Display for RecursiveError {
125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126        #[cfg(test)]
127        use std::ops::Deref;
128
129        let context = match self {
130            RecursiveError::Root { context, .. } => context,
131            RecursiveError::E2e { context, .. } => context,
132            RecursiveError::Mls { context, .. } => context,
133            RecursiveError::MlsClient { context, .. } => context,
134            RecursiveError::MlsConversation { context, .. } => context,
135            RecursiveError::MlsCredential { context, .. } => context,
136            RecursiveError::TransactionContext { context, .. } => context,
137            #[cfg(test)]
138            RecursiveError::Test(e) => return e.deref().fmt(f),
139        };
140        write!(f, "{}", context)
141    }
142}
143
144impl std::error::Error for RecursiveError {
145    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
146        match self {
147            RecursiveError::Root { source, .. } => Some(source.as_ref()),
148            RecursiveError::E2e { source, .. } => Some(source.as_ref()),
149            RecursiveError::Mls { source, .. } => Some(source.as_ref()),
150            RecursiveError::MlsClient { source, .. } => Some(source.as_ref()),
151            RecursiveError::MlsConversation { source, .. } => Some(source.as_ref()),
152            RecursiveError::MlsCredential { source, .. } => Some(source.as_ref()),
153            RecursiveError::TransactionContext { source, .. } => Some(source.as_ref()),
154            #[cfg(test)]
155            RecursiveError::Test(source) => Some(source.as_ref()),
156        }
157    }
158}
159
160/// Like [`Into`], but different, because we don't actually want to implement `Into` for our subordinate error types.
161///
162/// By forcing ourselves to map errors everywhere in order for question mark operators to work, we ensure that
163pub trait ToRecursiveError {
164    /// Construct a recursive error given the current context
165    fn construct_recursive(self, context: &'static str) -> RecursiveError;
166}
167
168macro_rules! impl_to_recursive_error_for {
169    ($($for:path => $variant:ident),+ $(,)?) => {
170        $(
171            impl ToRecursiveError for $for {
172                fn construct_recursive(self, context: &'static str) -> RecursiveError {
173                    RecursiveError::$variant {
174                        context,
175                        source: Box::new(self),
176                    }
177                }
178            }
179        )+
180    };
181}
182
183impl_to_recursive_error_for!(
184    crate::Error => Root,
185    crate::e2e_identity::Error => E2e,
186    crate::mls::Error => Mls,
187    crate::mls::session::Error => MlsClient,
188    crate::mls::conversation::Error => MlsConversation,
189    crate::mls::credential::Error => MlsCredential,
190    crate::transaction_context::Error => TransactionContext,
191);