askar_storage/
error.rs

1use std::error::Error as StdError;
2use std::fmt::{self, Display, Formatter};
3
4use crate::crypto::{Error as CryptoError, ErrorKind as CryptoErrorKind};
5
6/// The possible kinds of error produced by the crate
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub enum ErrorKind {
9    /// An unexpected error from the store backend
10    Backend,
11
12    /// The store backend was too busy to handle the request
13    Busy,
14
15    /// A custom error type for external integrations
16    Custom,
17
18    /// An insert operation failed due to a unique key conflict
19    Duplicate,
20
21    /// An encryption or decryption operation failed
22    Encryption,
23
24    /// The input parameters to the method were incorrect
25    Input,
26
27    /// The requested record was not found
28    NotFound,
29
30    /// An unexpected error occurred
31    Unexpected,
32
33    /// An unsupported operation was requested
34    Unsupported,
35}
36
37impl ErrorKind {
38    /// Convert the error kind to a string reference
39    pub fn as_str(&self) -> &'static str {
40        match self {
41            Self::Backend => "Backend error",
42            Self::Busy => "Busy",
43            Self::Custom => "Custom error",
44            Self::Duplicate => "Duplicate",
45            Self::Encryption => "Encryption error",
46            Self::Input => "Input error",
47            Self::NotFound => "Not found",
48            Self::Unexpected => "Unexpected error",
49            Self::Unsupported => "Unsupported",
50        }
51    }
52}
53
54impl Display for ErrorKind {
55    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
56        f.write_str(self.as_str())
57    }
58}
59
60/// The standard crate error type
61#[derive(Debug)]
62pub struct Error {
63    pub(crate) kind: ErrorKind,
64    pub(crate) cause: Option<Box<dyn StdError + Send + Sync + 'static>>,
65    pub(crate) message: Option<String>,
66}
67
68impl Error {
69    pub(crate) fn from_msg<T: Into<String>>(kind: ErrorKind, msg: T) -> Self {
70        Self {
71            kind,
72            cause: None,
73            message: Some(msg.into()),
74        }
75    }
76
77    /// Accessor for the error kind
78    pub fn kind(&self) -> ErrorKind {
79        self.kind
80    }
81
82    /// Accessor for the error message
83    pub fn message(&self) -> Option<&str> {
84        self.message.as_deref()
85    }
86
87    /// Split the error into its components
88    pub fn into_parts(
89        self,
90    ) -> (
91        ErrorKind,
92        Option<Box<dyn StdError + Send + Sync + 'static>>,
93        Option<String>,
94    ) {
95        (self.kind, self.cause, self.message)
96    }
97
98    pub(crate) fn with_cause<T: Into<Box<dyn StdError + Send + Sync + 'static>>>(
99        mut self,
100        err: T,
101    ) -> Self {
102        self.cause = Some(err.into());
103        self
104    }
105}
106
107impl Display for Error {
108    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
109        if let Some(msg) = self.message.as_ref() {
110            f.write_str(msg)?;
111        } else {
112            f.write_str(self.kind.as_str())?;
113        }
114        if let Some(cause) = self.cause.as_ref() {
115            write!(f, "\nCaused by: {}", cause)?;
116        }
117        Ok(())
118    }
119}
120
121impl StdError for Error {
122    fn source(&self) -> Option<&(dyn StdError + 'static)> {
123        self.cause
124            .as_ref()
125            .map(|err| &**err as &(dyn StdError + 'static))
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            cause: None,
140            message: None,
141        }
142    }
143}
144
145// FIXME would be preferable to remove this auto-conversion and handle
146// all sqlx errors manually, to ensure there is some context around the error
147#[cfg(any(feature = "postgres", feature = "sqlite"))]
148impl From<sqlx::Error> for Error {
149    fn from(err: sqlx::Error) -> Self {
150        Error::from(ErrorKind::Backend).with_cause(err)
151    }
152}
153
154impl From<CryptoError> for Error {
155    fn from(err: CryptoError) -> Self {
156        let kind = match err.kind() {
157            CryptoErrorKind::Custom => ErrorKind::Custom,
158            CryptoErrorKind::Encryption => ErrorKind::Encryption,
159            CryptoErrorKind::ExceededBuffer | CryptoErrorKind::Unexpected => ErrorKind::Unexpected,
160            CryptoErrorKind::Invalid
161            | CryptoErrorKind::InvalidKeyData
162            | CryptoErrorKind::InvalidNonce
163            | CryptoErrorKind::MissingSecretKey
164            | CryptoErrorKind::Usage => ErrorKind::Input,
165            CryptoErrorKind::Unsupported => ErrorKind::Unsupported,
166        };
167        Error::from_msg(kind, err.message())
168    }
169}
170
171macro_rules! err_msg {
172    () => {
173        $crate::error::Error::from($crate::error::ErrorKind::Input)
174    };
175    ($kind:ident) => {
176        $crate::error::Error::from($crate::error::ErrorKind::$kind)
177    };
178    ($kind:ident, $($args:tt)+) => {
179        $crate::error::Error::from_msg($crate::error::ErrorKind::$kind, format!($($args)+))
180    };
181    ($($args:tt)+) => {
182        $crate::error::Error::from_msg($crate::error::ErrorKind::Input, format!($($args)+))
183    };
184}
185
186macro_rules! err_map {
187    ($($params:tt)*) => {
188        |err| err_msg!($($params)*).with_cause(err)
189    };
190}