use crate::errors::{ErrorSeverity, UserFriendlyError};
use std::collections::hash_map::DefaultHasher;
use std::fmt;
use std::hash::{Hash, Hasher};
use thiserror::Error;
#[derive(Debug, Clone)]
pub enum HashingOperation {
Hash,
Verify,
}
impl fmt::Display for HashingOperation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HashingOperation::Hash => write!(f, "hash"),
HashingOperation::Verify => write!(f, "verify"),
}
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum HashingError {
#[error("Hashing error: {operation} - {message}")]
Operation {
operation: HashingOperation,
message: String,
algorithm: Option<String>,
expected_format: Option<String>,
},
}
impl HashingError {
pub fn new(operation: HashingOperation, message: impl Into<String>) -> Self {
HashingError::Operation {
operation,
message: message.into(),
algorithm: None,
expected_format: None,
}
}
pub fn with_algorithm(
operation: HashingOperation,
message: impl Into<String>,
algorithm: impl Into<String>,
) -> Self {
HashingError::Operation {
operation,
message: message.into(),
algorithm: Some(algorithm.into()),
expected_format: None,
}
}
pub fn with_context(
operation: HashingOperation,
message: impl Into<String>,
algorithm: Option<String>,
expected_format: Option<String>,
) -> Self {
HashingError::Operation {
operation,
message: message.into(),
algorithm,
expected_format,
}
}
fn support_code_inner(&self) -> String {
let mut hasher = DefaultHasher::new();
match self {
HashingError::Operation {
operation,
algorithm,
..
} => {
format!("HASH-{}-{:X}", operation.to_string().to_uppercase(), {
format!("{:?}{:?}", operation, algorithm).hash(&mut hasher);
hasher.finish() % 10000
})
}
}
}
}
impl UserFriendlyError for HashingError {
fn user_message(&self) -> String {
match self {
HashingError::Operation { operation, .. } => match operation {
HashingOperation::Hash => {
"There's an issue with the security processing system. Please try again in a moment."
.to_string()
}
HashingOperation::Verify => {
"We couldn't verify your credentials due to a technical issue. Please try signing in again."
.to_string()
}
},
}
}
fn developer_message(&self) -> String {
match self {
HashingError::Operation {
operation,
message,
algorithm,
expected_format,
} => {
let algorithm_context = algorithm
.as_ref()
.map(|a| format!(" [Algorithm: {}]", a))
.unwrap_or_default();
let format_context = expected_format
.as_ref()
.map(|ef| format!(" [Expected: {}]", ef))
.unwrap_or_default();
format!(
"Hash operation {} failed: {}{}{}",
operation, message, algorithm_context, format_context
)
}
}
}
fn support_code(&self) -> String {
self.support_code_inner()
}
fn severity(&self) -> ErrorSeverity {
match self {
HashingError::Operation { operation, .. } => match operation {
HashingOperation::Hash => ErrorSeverity::Critical,
HashingOperation::Verify => ErrorSeverity::Critical,
},
}
}
fn suggested_actions(&self) -> Vec<String> {
match self {
HashingError::Operation { operation, .. } => match operation {
HashingOperation::Hash => vec![
"This is a critical security system error".to_string(),
"Contact our support team immediately".to_string(),
"Do not retry operations that involve password or secret changes".to_string(),
"Use secure communication when reporting this issue".to_string(),
],
HashingOperation::Verify => vec![
"Double-check your password for typos".to_string(),
"Ensure Caps Lock is not accidentally enabled".to_string(),
"If you're certain your password is correct, contact support".to_string(),
"Try using password recovery if verification continues to fail".to_string(),
],
},
}
}
fn is_retryable(&self) -> bool {
match self {
HashingError::Operation { operation, .. } => match operation {
HashingOperation::Hash => false, HashingOperation::Verify => true, },
}
}
}