use thiserror::Error;
#[derive(Error, Debug)]
pub enum NonceError {
#[error("Nonce already exists")]
DuplicateNonce,
#[error("Invalid signature")]
InvalidSignature,
#[error("Timestamp out of window")]
TimestampOutOfWindow,
#[error("Storage error: {0}")]
StorageError(#[source] Box<dyn std::error::Error + Send + Sync>),
#[error("Crypto error: {0}")]
CryptoError(String),
}
impl NonceError {
pub fn code(&self) -> &'static str {
match self {
NonceError::DuplicateNonce => "duplicate_nonce",
NonceError::InvalidSignature => "invalid_signature",
NonceError::TimestampOutOfWindow => "timestamp_out_of_window",
NonceError::StorageError(_) => "storage_error",
NonceError::CryptoError(_) => "crypto_error",
}
}
pub fn from_storage_error<E>(error: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
NonceError::StorageError(Box::new(error))
}
pub fn from_storage_message<S: Into<String>>(message: S) -> Self {
#[derive(Debug)]
struct SimpleError(String);
impl std::fmt::Display for SimpleError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::error::Error for SimpleError {}
NonceError::StorageError(Box::new(SimpleError(message.into())))
}
pub fn is_temporary(&self) -> bool {
matches!(
self,
NonceError::StorageError(_) | NonceError::CryptoError(_)
)
}
pub fn is_authentication_error(&self) -> bool {
matches!(
self,
NonceError::DuplicateNonce
| NonceError::InvalidSignature
| NonceError::TimestampOutOfWindow
)
}
pub fn is_client_error(&self) -> bool {
matches!(
self,
NonceError::DuplicateNonce
| NonceError::InvalidSignature
| NonceError::TimestampOutOfWindow
)
}
pub fn is_server_error(&self) -> bool {
matches!(
self,
NonceError::StorageError(_) | NonceError::CryptoError(_)
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error;
#[test]
fn test_error_types() {
assert_eq!(
NonceError::DuplicateNonce.to_string(),
"Nonce already exists"
);
assert_eq!(
NonceError::InvalidSignature.to_string(),
"Invalid signature"
);
assert_eq!(
NonceError::TimestampOutOfWindow.to_string(),
"Timestamp out of window"
);
let storage_error = NonceError::from_storage_message("test error");
assert_eq!(storage_error.to_string(), "Storage error: test error");
let crypto_error = NonceError::CryptoError("crypto test error".to_string());
assert_eq!(crypto_error.to_string(), "Crypto error: crypto test error");
}
#[test]
fn test_error_codes() {
assert_eq!(NonceError::DuplicateNonce.code(), "duplicate_nonce");
assert_eq!(NonceError::InvalidSignature.code(), "invalid_signature");
assert_eq!(
NonceError::TimestampOutOfWindow.code(),
"timestamp_out_of_window"
);
assert_eq!(
NonceError::from_storage_message("test").code(),
"storage_error"
);
assert_eq!(
NonceError::CryptoError("test".to_string()).code(),
"crypto_error"
);
}
#[test]
fn test_storage_error_from_error() {
use std::io;
let io_error = io::Error::new(io::ErrorKind::PermissionDenied, "Permission denied");
let nonce_error = NonceError::from_storage_error(io_error);
assert_eq!(nonce_error.code(), "storage_error");
assert!(nonce_error.source().is_some());
assert!(nonce_error.to_string().contains("Permission denied"));
}
#[test]
fn test_storage_error_from_message() {
let error = NonceError::from_storage_message("Connection timeout");
assert_eq!(error.code(), "storage_error");
assert!(error.to_string().contains("Connection timeout"));
}
#[test]
fn test_error_classification() {
let auth_errors = [
NonceError::DuplicateNonce,
NonceError::InvalidSignature,
NonceError::TimestampOutOfWindow,
];
for error in &auth_errors {
assert!(error.is_authentication_error());
assert!(error.is_client_error());
assert!(!error.is_server_error());
assert!(!error.is_temporary());
}
let system_errors = [
NonceError::from_storage_message("test"),
NonceError::CryptoError("test".to_string()),
];
for error in &system_errors {
assert!(!error.is_authentication_error());
assert!(!error.is_client_error());
assert!(error.is_server_error());
assert!(error.is_temporary());
}
}
}