use core::fmt;
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum ErrorKind {
InvalidLength,
InvalidParameter,
AuthenticationFailed,
Unsupported,
NotApprovedInFipsMode,
SelfTestFailed,
ModuleErrorState,
EntropyFailure,
CounterExhausted,
MalformedEncoding,
Internal,
}
impl ErrorKind {
pub const ALL: &'static [ErrorKind] = &[
ErrorKind::InvalidLength,
ErrorKind::InvalidParameter,
ErrorKind::AuthenticationFailed,
ErrorKind::MalformedEncoding,
ErrorKind::Unsupported,
ErrorKind::NotApprovedInFipsMode,
ErrorKind::SelfTestFailed,
ErrorKind::ModuleErrorState,
ErrorKind::EntropyFailure,
ErrorKind::CounterExhausted,
ErrorKind::Internal,
];
pub const fn id(self) -> &'static str {
match self {
Self::InvalidLength => "invalid-length",
Self::InvalidParameter => "invalid-parameter",
Self::AuthenticationFailed => "authentication-failed",
Self::Unsupported => "unsupported",
Self::NotApprovedInFipsMode => "not-approved-in-fips-mode",
Self::SelfTestFailed => "self-test-failed",
Self::ModuleErrorState => "module-error-state",
Self::EntropyFailure => "entropy-failure",
Self::CounterExhausted => "counter-exhausted",
Self::MalformedEncoding => "malformed-encoding",
Self::Internal => "internal",
}
}
#[must_use]
pub const fn retryable(self) -> bool {
matches!(self, Self::EntropyFailure)
}
#[must_use]
pub const fn caller_correctable(self) -> bool {
matches!(
self,
Self::InvalidLength
| Self::InvalidParameter
| Self::MalformedEncoding
| Self::Unsupported
| Self::NotApprovedInFipsMode
| Self::CounterExhausted
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Error {
kind: ErrorKind,
context: &'static str,
}
impl Error {
pub const fn new(kind: ErrorKind, context: &'static str) -> Self {
Self { kind, context }
}
pub const fn kind(&self) -> ErrorKind {
self.kind
}
pub const fn context(&self) -> &'static str {
self.context
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.kind.id(), self.context)
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
#[macro_export]
macro_rules! err {
($kind:ident, $ctx:literal) => {
$crate::Error::new($crate::ErrorKind::$kind, $ctx)
};
}
#[macro_export]
macro_rules! ensure {
($cond:expr, $kind:ident, $ctx:literal) => {
if !($cond) {
return Err($crate::Error::new($crate::ErrorKind::$kind, $ctx));
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_variant_list_is_complete() {
#[allow(clippy::needless_match)]
fn identify(kind: ErrorKind) -> ErrorKind {
match kind {
ErrorKind::InvalidLength => ErrorKind::InvalidLength,
ErrorKind::InvalidParameter => ErrorKind::InvalidParameter,
ErrorKind::AuthenticationFailed => ErrorKind::AuthenticationFailed,
ErrorKind::MalformedEncoding => ErrorKind::MalformedEncoding,
ErrorKind::Unsupported => ErrorKind::Unsupported,
ErrorKind::NotApprovedInFipsMode => ErrorKind::NotApprovedInFipsMode,
ErrorKind::SelfTestFailed => ErrorKind::SelfTestFailed,
ErrorKind::ModuleErrorState => ErrorKind::ModuleErrorState,
ErrorKind::EntropyFailure => ErrorKind::EntropyFailure,
ErrorKind::CounterExhausted => ErrorKind::CounterExhausted,
ErrorKind::Internal => ErrorKind::Internal,
}
}
assert_eq!(ErrorKind::ALL.len(), 11);
for kind in ErrorKind::ALL {
assert_eq!(identify(*kind), *kind);
}
for (i, a) in ErrorKind::ALL.iter().enumerate() {
for b in ErrorKind::ALL.iter().skip(i + 1) {
assert_ne!(a.id(), b.id(), "two kinds share an identifier");
}
assert!(!a.id().is_empty());
}
}
}