core_crypto/error/
recursive.rs

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