core_crypto/error/
recursive.rs

1// Wire
2// Copyright (C) 2022 Wire Swiss GmbH
3
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see http://www.gnu.org/licenses/.
16
17/// These errors wrap each of the module-specific errors in CoreCrypto.
18///
19/// The goal here is to reduce the need to redeclare each of these error
20/// types as an individual variant of a module-specific error type.
21#[derive(Debug)]
22pub enum RecursiveError {
23    /// Wrap a [crate::Error] for recursion.
24    Root {
25        /// What was happening in the caller
26        context: &'static str,
27        /// What happened
28        source: Box<crate::Error>,
29    },
30    /// Wrap a [crate::e2e_identity::Error] for recursion.
31    E2e {
32        /// What was happening in the caller
33        context: &'static str,
34        /// What happened
35        source: Box<crate::e2e_identity::Error>,
36    },
37    /// Wrap a [crate::mls::Error] for recursion.
38    Mls {
39        /// What was happening in the caller
40        context: &'static str,
41        /// What happened
42        source: Box<crate::mls::Error>,
43    },
44    /// Wrap a [crate::mls::client::Error] for recursion.
45    MlsClient {
46        /// What was happening in the caller
47        context: &'static str,
48        /// What happened
49        source: Box<crate::mls::client::Error>,
50    },
51    /// Wrap a [crate::mls::conversation::Error] for recursion.
52    MlsConversation {
53        /// What was happening in the caller
54        context: &'static str,
55        /// What happened
56        source: Box<crate::mls::conversation::Error>,
57    },
58    /// Wrap a [crate::mls::credential::Error] for recursion.
59    MlsCredential {
60        /// What was happening in the caller
61        context: &'static str,
62        /// What happened
63        source: Box<crate::mls::credential::Error>,
64    },
65    /// Wrap a [crate::test_utils::TestError] for recursion.
66    #[cfg(test)]
67    Test(Box<crate::test_utils::TestError>),
68}
69
70impl RecursiveError {
71    /// Convert a [crate::Error] into a [RecursiveError], with context
72    pub fn root<E: Into<crate::Error>>(context: &'static str) -> impl FnOnce(E) -> Self {
73        move |into_source| Self::Root {
74            context,
75            source: Box::new(into_source.into()),
76        }
77    }
78
79    /// Convert a [crate::e2e_identity::Error] into a [RecursiveError], with context
80    pub fn e2e_identity<E: Into<crate::e2e_identity::Error>>(context: &'static str) -> impl FnOnce(E) -> Self {
81        move |into_source| Self::E2e {
82            context,
83            source: Box::new(into_source.into()),
84        }
85    }
86
87    /// Convert a [crate::mls::Error] into a [RecursiveError], with context
88    pub fn mls<E: Into<crate::mls::Error>>(context: &'static str) -> impl FnOnce(E) -> Self {
89        move |into_source| Self::Mls {
90            context,
91            source: Box::new(into_source.into()),
92        }
93    }
94
95    /// Convert a [crate::mls::client::Error] into a [RecursiveError], with context
96    pub fn mls_client<E: Into<crate::mls::client::Error>>(context: &'static str) -> impl FnOnce(E) -> Self {
97        move |into_source| Self::MlsClient {
98            context,
99            source: Box::new(into_source.into()),
100        }
101    }
102
103    /// Convert a [crate::mls::conversation::Error] into a [RecursiveError], with context
104    pub fn mls_conversation<E: Into<crate::mls::conversation::Error>>(context: &'static str) -> impl FnOnce(E) -> Self {
105        move |into_source| Self::MlsConversation {
106            context,
107            source: Box::new(into_source.into()),
108        }
109    }
110
111    /// Convert a [crate::mls::credential::Error] into a [RecursiveError], with context
112    pub fn mls_credential<E: Into<crate::mls::credential::Error>>(context: &'static str) -> impl FnOnce(E) -> Self {
113        move |into_source| Self::MlsCredential {
114            context,
115            source: Box::new(into_source.into()),
116        }
117    }
118
119    #[cfg(test)]
120    pub(crate) fn test<E: Into<crate::test_utils::TestError>>() -> impl FnOnce(E) -> Self {
121        move |into_source| Self::Test(Box::new(into_source.into()))
122    }
123}
124
125impl std::fmt::Display for RecursiveError {
126    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127        #[cfg(test)]
128        use std::ops::Deref;
129
130        let context = match self {
131            RecursiveError::Root { context, .. } => context,
132            RecursiveError::E2e { context, .. } => context,
133            RecursiveError::Mls { context, .. } => context,
134            RecursiveError::MlsClient { context, .. } => context,
135            RecursiveError::MlsConversation { context, .. } => context,
136            RecursiveError::MlsCredential { 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            #[cfg(test)]
154            RecursiveError::Test(source) => Some(source.as_ref()),
155        }
156    }
157}
158
159/// Like [`Into`], but different, because we don't actually want to implement `Into` for our subordinate error types.
160///
161/// By forcing ourselves to map errors everywhere in order for question mark operators to work, we ensure that
162pub trait ToRecursiveError {
163    /// Construct a recursive error given the current context
164    fn construct_recursive(self, context: &'static str) -> RecursiveError;
165}
166
167macro_rules! impl_to_recursive_error_for {
168    ($($for:path => $variant:ident),+ $(,)?) => {
169        $(
170            impl ToRecursiveError for $for {
171                fn construct_recursive(self, context: &'static str) -> RecursiveError {
172                    RecursiveError::$variant {
173                        context,
174                        source: Box::new(self),
175                    }
176                }
177            }
178        )+
179    };
180}
181
182impl_to_recursive_error_for!(
183    crate::Error => Root,
184    crate::e2e_identity::Error => E2e,
185    crate::mls::Error => Mls,
186    crate::mls::client::Error => MlsClient,
187    crate::mls::conversation::Error => MlsConversation,
188    crate::mls::credential::Error => MlsCredential,
189);