use std::fmt;
use std::time::Duration;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
PasswordHash(PasswordHashError),
Token(TokenError),
Validation(ValidationError),
Config(ConfigError),
Storage(StorageError),
Crypto(CryptoError),
RateLimitExceeded {
retry_after: Duration,
},
Internal(String),
Other(String),
}
impl Error {
pub fn internal(msg: impl Into<String>) -> Self {
Error::Internal(msg.into())
}
pub fn validation(msg: impl Into<String>) -> Self {
Error::Validation(ValidationError::Custom(msg.into()))
}
pub fn rate_limited(retry_after: Duration) -> Self {
Error::RateLimitExceeded { retry_after }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PasswordHashError {
HashFailed(String),
VerifyFailed,
InvalidFormat(String),
UnsupportedAlgorithm(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TokenError {
Expired,
InvalidFormat(String),
InvalidSignature,
EncodingFailed(String),
DecodingFailed(String),
MissingClaim(String),
InvalidClaim(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ValidationError {
PasswordTooShort { min_length: usize, actual: usize },
PasswordTooLong { max_length: usize, actual: usize },
PasswordTooWeak(String),
InvalidEmail(String),
InvalidUsername(String),
EmptyField(String),
Custom(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConfigError {
MissingRequired(String),
InvalidValue { key: String, message: String },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StorageError {
ConnectionFailed(String),
NotFound(String),
AlreadyExists(String),
OperationFailed(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CryptoError {
RngFailed(String),
InvalidKey(String),
EncryptionFailed(String),
DecryptionFailed(String),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::PasswordHash(e) => write!(f, "Password hash error: {}", e),
Error::Token(e) => write!(f, "Token error: {}", e),
Error::Validation(e) => write!(f, "Validation error: {}", e),
Error::Config(e) => write!(f, "Config error: {}", e),
Error::Storage(e) => write!(f, "Storage error: {}", e),
Error::Crypto(e) => write!(f, "Crypto error: {}", e),
Error::RateLimitExceeded { retry_after } => {
write!(f, "Rate limit exceeded, retry after {:?}", retry_after)
}
Error::Internal(msg) => write!(f, "Internal error: {}", msg),
Error::Other(msg) => write!(f, "Error: {}", msg),
}
}
}
impl fmt::Display for PasswordHashError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PasswordHashError::HashFailed(msg) => write!(f, "hash generation failed: {}", msg),
PasswordHashError::VerifyFailed => write!(f, "password verification failed"),
PasswordHashError::InvalidFormat(msg) => write!(f, "invalid hash format: {}", msg),
PasswordHashError::UnsupportedAlgorithm(alg) => {
write!(f, "unsupported algorithm: {}", alg)
}
}
}
}
impl fmt::Display for TokenError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TokenError::Expired => write!(f, "token has expired"),
TokenError::InvalidFormat(msg) => write!(f, "invalid token format: {}", msg),
TokenError::InvalidSignature => write!(f, "invalid token signature"),
TokenError::EncodingFailed(msg) => write!(f, "token encoding failed: {}", msg),
TokenError::DecodingFailed(msg) => write!(f, "token decoding failed: {}", msg),
TokenError::MissingClaim(claim) => write!(f, "missing required claim: {}", claim),
TokenError::InvalidClaim(msg) => write!(f, "invalid claim value: {}", msg),
}
}
}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ValidationError::PasswordTooShort { min_length, actual } => {
write!(
f,
"password too short: minimum {} characters, got {}",
min_length, actual
)
}
ValidationError::PasswordTooLong { max_length, actual } => {
write!(
f,
"password too long: maximum {} characters, got {}",
max_length, actual
)
}
ValidationError::PasswordTooWeak(msg) => write!(f, "password too weak: {}", msg),
ValidationError::InvalidEmail(email) => write!(f, "invalid email format: {}", email),
ValidationError::InvalidUsername(name) => {
write!(f, "invalid username format: {}", name)
}
ValidationError::EmptyField(field) => write!(f, "field '{}' cannot be empty", field),
ValidationError::Custom(msg) => write!(f, "{}", msg),
}
}
}
impl fmt::Display for ConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ConfigError::MissingRequired(key) => {
write!(f, "missing required configuration: {}", key)
}
ConfigError::InvalidValue { key, message } => {
write!(f, "invalid configuration value for '{}': {}", key, message)
}
}
}
}
impl fmt::Display for StorageError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StorageError::ConnectionFailed(msg) => write!(f, "storage connection failed: {}", msg),
StorageError::NotFound(item) => write!(f, "not found: {}", item),
StorageError::AlreadyExists(item) => write!(f, "already exists: {}", item),
StorageError::OperationFailed(msg) => write!(f, "storage operation failed: {}", msg),
}
}
}
impl fmt::Display for CryptoError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CryptoError::RngFailed(msg) => write!(f, "random number generation failed: {}", msg),
CryptoError::InvalidKey(msg) => write!(f, "invalid key: {}", msg),
CryptoError::EncryptionFailed(msg) => write!(f, "encryption failed: {}", msg),
CryptoError::DecryptionFailed(msg) => write!(f, "decryption failed: {}", msg),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
impl std::error::Error for PasswordHashError {}
impl std::error::Error for TokenError {}
impl std::error::Error for ValidationError {}
impl std::error::Error for ConfigError {}
impl std::error::Error for StorageError {}
impl std::error::Error for CryptoError {}
impl From<PasswordHashError> for Error {
fn from(err: PasswordHashError) -> Self {
Error::PasswordHash(err)
}
}
impl From<TokenError> for Error {
fn from(err: TokenError) -> Self {
Error::Token(err)
}
}
impl From<ValidationError> for Error {
fn from(err: ValidationError) -> Self {
Error::Validation(err)
}
}
impl From<ConfigError> for Error {
fn from(err: ConfigError) -> Self {
Error::Config(err)
}
}
impl From<StorageError> for Error {
fn from(err: StorageError) -> Self {
Error::Storage(err)
}
}
impl From<CryptoError> for Error {
fn from(err: CryptoError) -> Self {
Error::Crypto(err)
}
}
impl From<String> for Error {
fn from(msg: String) -> Self {
Error::Other(msg)
}
}
impl From<&str> for Error {
fn from(msg: &str) -> Self {
Error::Other(msg.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = Error::PasswordHash(PasswordHashError::VerifyFailed);
assert_eq!(
err.to_string(),
"Password hash error: password verification failed"
);
}
#[test]
fn test_error_from_password_hash() {
let hash_err = PasswordHashError::HashFailed("test".to_string());
let err: Error = hash_err.into();
assert!(matches!(err, Error::PasswordHash(_)));
}
#[test]
fn test_validation_error_display() {
let err = ValidationError::PasswordTooShort {
min_length: 8,
actual: 4,
};
assert_eq!(
err.to_string(),
"password too short: minimum 8 characters, got 4"
);
}
#[test]
fn test_token_error_display() {
let err = TokenError::Expired;
assert_eq!(err.to_string(), "token has expired");
}
}