askar_crypto/
error.rs

1#[cfg(feature = "std")]
2use alloc::boxed::Box;
3use core::fmt::{self, Display, Formatter};
4
5#[cfg(feature = "std")]
6use std::error::Error as StdError;
7
8/// The possible kinds of error produced by the crate
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum ErrorKind {
11    /// Custom error, for use external integrations
12    Custom,
13
14    /// An encryption or decryption operation failed
15    Encryption,
16
17    /// Out of space in provided buffer
18    ExceededBuffer,
19
20    /// The provided input was invalid
21    Invalid,
22
23    /// The provided key was invalid
24    InvalidKeyData,
25
26    /// The provided nonce was invalid (bad length)
27    InvalidNonce,
28
29    /// A secret key is required but not present
30    MissingSecretKey,
31
32    /// An unexpected error occurred
33    Unexpected,
34
35    /// The input parameters to the method were incorrect
36    Usage,
37
38    /// An unsupported operation was requested
39    Unsupported,
40}
41
42impl ErrorKind {
43    /// Convert the error kind to a string reference
44    pub fn as_str(&self) -> &'static str {
45        match self {
46            Self::Custom => "Custom error",
47            Self::Encryption => "Encryption error",
48            Self::ExceededBuffer => "Exceeded buffer size",
49            Self::Invalid => "Invalid input",
50            Self::InvalidNonce => "Invalid encryption nonce",
51            Self::InvalidKeyData => "Invalid key data",
52            Self::MissingSecretKey => "Missing secret key",
53            Self::Unexpected => "Unexpected error",
54            Self::Usage => "Usage error",
55            Self::Unsupported => "Unsupported",
56        }
57    }
58}
59
60impl Display for ErrorKind {
61    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
62        f.write_str(self.as_str())
63    }
64}
65
66/// The standard crate error type
67#[derive(Debug)]
68pub struct Error {
69    pub(crate) kind: ErrorKind,
70    #[cfg(feature = "std")]
71    pub(crate) cause: Option<Box<dyn StdError + Send + Sync + 'static>>,
72    pub(crate) message: Option<&'static str>,
73}
74
75impl Error {
76    /// Create a new error instance with message text
77    pub fn from_msg(kind: ErrorKind, msg: &'static str) -> Self {
78        Self {
79            kind,
80            #[cfg(feature = "std")]
81            cause: None,
82            message: Some(msg),
83        }
84    }
85
86    /// Accessor for the error kind
87    pub fn kind(&self) -> ErrorKind {
88        self.kind
89    }
90
91    /// Accessor for the error message
92    pub fn message(&self) -> &'static str {
93        self.message.unwrap_or_else(|| self.kind.as_str())
94    }
95
96    #[cfg(feature = "std")]
97    pub(crate) fn with_cause<T: Into<Box<dyn StdError + Send + Sync>>>(mut self, err: T) -> Self {
98        self.cause = Some(err.into());
99        self
100    }
101}
102
103impl Display for Error {
104    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
105        if let Some(msg) = self.message {
106            f.write_str(msg)?;
107        } else {
108            f.write_str(self.kind.as_str())?;
109        }
110        #[cfg(feature = "std")]
111        if let Some(cause) = self.cause.as_ref() {
112            write!(f, "\nCaused by: {}", cause)?;
113        }
114        Ok(())
115    }
116}
117
118#[cfg(feature = "std")]
119impl StdError for Error {
120    fn source(&self) -> Option<&(dyn StdError + 'static)> {
121        self.cause.as_ref().map(|err| {
122            // &<Error + ?Sized> implements Error, which lets us
123            // create a new trait object
124            (&**err) as &dyn StdError
125        })
126    }
127}
128
129impl PartialEq for Error {
130    fn eq(&self, other: &Self) -> bool {
131        self.kind == other.kind && self.message == other.message
132    }
133}
134
135impl From<ErrorKind> for Error {
136    fn from(kind: ErrorKind) -> Self {
137        Self {
138            kind,
139            #[cfg(feature = "std")]
140            cause: None,
141            message: None,
142        }
143    }
144}
145
146#[macro_export]
147/// Assemble an error kind and optional message
148macro_rules! err_msg {
149    ($kind:ident) => {
150        $crate::Error::from($crate::ErrorKind::$kind)
151    };
152    ($kind:ident, $msg:expr) => {
153        $crate::Error::from_msg($crate::ErrorKind::$kind, $msg)
154    };
155}
156
157#[cfg(feature = "std")]
158/// Map an external error
159#[macro_export]
160macro_rules! err_map {
161    ($($params:tt)*) => {
162        |err| err_msg!($($params)*).with_cause(err)
163    };
164}
165
166#[cfg(not(feature = "std"))]
167/// Map an external error
168#[macro_export]
169macro_rules! err_map {
170    ($($params:tt)*) => {
171        |_| err_msg!($($params)*)
172    };
173}
174
175#[cfg(all(test, feature = "std"))]
176mod tests {
177    use super::*;
178
179    #[test]
180    // ensure that the source can still be downcast
181    fn downcast_err() {
182        #[derive(Debug)]
183        struct E;
184        impl Display for E {
185            fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
186                write!(f, "E")
187            }
188        }
189        impl StdError for E {}
190
191        let err = Error::from(ErrorKind::Unexpected).with_cause(E);
192        let e = err.source().unwrap().downcast_ref::<E>();
193        assert!(e.is_some());
194    }
195}