axum_gate/secrets/
errors.rs

1//! Secret-category native errors.
2//!
3//! Category-native error type for secret storage and hashing concerns,
4//! aligning with the crate's categorical, domain-driven structure.
5//! Use these errors directly in handlers and services.
6//!
7//! # Overview
8//! - `SecretError`: category-native error enum for hashing flows
9//! - Uses `HashingOperation` for hashing context
10//!
11//! # Examples
12//!
13//!
14//!
15//! Hashing failure with algorithm context:
16//! ```rust
17//! use axum_gate::secrets::SecretError;
18//! use axum_gate::hashing::HashingOperation;
19//!
20//! let err = SecretError::hashing_with_algorithm(
21//!     HashingOperation::Verify,
22//!     "password verification failed",
23//!     "argon2id",
24//! );
25//! ```
26
27use crate::errors::{ErrorSeverity, UserFriendlyError};
28use crate::hashing::HashingOperation;
29use std::collections::hash_map::DefaultHasher;
30use std::hash::{Hash, Hasher};
31use thiserror::Error;
32
33/// Secret-category native errors.
34///
35/// Use for secret repository and hashing/verification flows.
36#[derive(Debug, Error)]
37#[non_exhaustive]
38pub enum SecretError {
39    //
40    /// Hashing/verification error in secret flow.
41    #[error("Secret hashing error: {operation} - {message}")]
42    Hashing {
43        /// The hashing operation that failed.
44        operation: HashingOperation,
45        /// Description of the failure (non-sensitive).
46        message: String,
47        /// The hashing algorithm used (e.g., `argon2id`) if known.
48        algorithm: Option<String>,
49        /// Expected hash format (sanitized) if relevant.
50        expected_format: Option<String>,
51    },
52}
53
54impl SecretError {
55    /// Construct a hashing error with algorithm context.
56    pub fn hashing_with_algorithm(
57        operation: HashingOperation,
58        message: impl Into<String>,
59        algorithm: impl Into<String>,
60    ) -> Self {
61        SecretError::Hashing {
62            operation,
63            message: message.into(),
64            algorithm: Some(algorithm.into()),
65            expected_format: None,
66        }
67    }
68
69    /// Construct a hashing error with full context.
70    pub fn hashing_with_context(
71        operation: HashingOperation,
72        message: impl Into<String>,
73        algorithm: Option<String>,
74        expected_format: Option<String>,
75    ) -> Self {
76        SecretError::Hashing {
77            operation,
78            message: message.into(),
79            algorithm,
80            expected_format,
81        }
82    }
83
84    fn support_code_inner(&self) -> String {
85        let mut hasher = DefaultHasher::new();
86        match self {
87            SecretError::Hashing {
88                operation,
89                algorithm,
90                ..
91            } => {
92                format!("SECR-HASH-{}-{:X}", operation.to_string().to_uppercase(), {
93                    format!("{:?}{:?}", operation, algorithm).hash(&mut hasher);
94                    hasher.finish() % 10000
95                })
96            }
97        }
98    }
99}
100
101impl UserFriendlyError for SecretError {
102    fn user_message(&self) -> String {
103        match self {
104            SecretError::Hashing { operation, .. } => match operation {
105                HashingOperation::Hash => "There's an issue with the security processing system. Please try again in a moment.".to_string(),
106                HashingOperation::Verify => "We couldn't verify your credentials due to a technical issue. Please try signing in again.".to_string(),
107            },
108        }
109    }
110
111    fn developer_message(&self) -> String {
112        match self {
113            SecretError::Hashing {
114                operation,
115                message,
116                algorithm,
117                expected_format,
118            } => {
119                let algo_s = algorithm
120                    .as_ref()
121                    .map(|a| format!(" [Algorithm: {}]", a))
122                    .unwrap_or_default();
123                let exp_s = expected_format
124                    .as_ref()
125                    .map(|e| format!(" [Expected: {}]", e))
126                    .unwrap_or_default();
127                format!(
128                    "Secret hashing {} failed: {}{}{}",
129                    operation, message, algo_s, exp_s
130                )
131            }
132        }
133    }
134
135    fn support_code(&self) -> String {
136        self.support_code_inner()
137    }
138
139    fn severity(&self) -> ErrorSeverity {
140        match self {
141            SecretError::Hashing { .. } => ErrorSeverity::Critical,
142        }
143    }
144
145    fn suggested_actions(&self) -> Vec<String> {
146        match self {
147            SecretError::Hashing { operation, .. } => match operation {
148                HashingOperation::Hash => vec![
149                    "This is a critical security system error".to_string(),
150                    "Contact our support team immediately".to_string(),
151                    "Do not retry operations that involve password or secret changes".to_string(),
152                ],
153                HashingOperation::Verify => vec![
154                    "Double-check your password for typos".to_string(),
155                    "Ensure Caps Lock is not accidentally enabled".to_string(),
156                    "If you're certain your password is correct, contact support".to_string(),
157                    "Try using password recovery if verification continues to fail".to_string(),
158                ],
159            },
160        }
161    }
162
163    fn is_retryable(&self) -> bool {
164        match self {
165            SecretError::Hashing { operation, .. } => match operation {
166                HashingOperation::Hash => false,
167                HashingOperation::Verify => true, // user can correct input
168            },
169        }
170    }
171}