Skip to main content

secure_errors/
classify.rs

1//! Error classification — retryability, alerting, security signals.
2//!
3//! Every `AppError` variant maps to exactly one `ErrorClassification`.
4
5use crate::kind::AppError;
6
7/// Operational classification of an error.
8///
9/// This type is `#[non_exhaustive]` to allow additional flags in future milestones
10/// without breaking downstream match expressions.
11///
12/// # Examples
13///
14/// ```
15/// use secure_errors::classify::ErrorClassification;
16/// use secure_errors::kind::AppError;
17///
18/// let cls = ErrorClassification::for_error(&AppError::Forbidden { policy: "admin_only" });
19/// assert!(cls.is_security_signal());
20/// assert!(cls.is_alertable());
21/// assert!(!cls.is_retryable());
22/// ```
23#[must_use]
24#[derive(Debug, Clone, PartialEq, Eq)]
25#[non_exhaustive]
26#[allow(clippy::struct_excessive_bools)]
27pub struct ErrorClassification {
28    retryable: bool,
29    alertable: bool,
30    security_signal: bool,
31    user_fixable: bool,
32}
33
34impl ErrorClassification {
35    /// Returns the classification for the given `AppError`.
36    pub fn for_error(err: &AppError) -> Self {
37        match err {
38            AppError::Validation { .. } | AppError::NotFound | AppError::Conflict => Self {
39                retryable: false,
40                alertable: false,
41                security_signal: false,
42                user_fixable: true,
43            },
44            AppError::Forbidden { .. } | AppError::Crypto => Self {
45                retryable: false,
46                alertable: true,
47                security_signal: true,
48                user_fixable: false,
49            },
50            AppError::Dependency { .. } => Self {
51                retryable: true,
52                alertable: true,
53                security_signal: false,
54                user_fixable: false,
55            },
56            AppError::Internal => Self {
57                retryable: false,
58                alertable: true,
59                security_signal: false,
60                user_fixable: false,
61            },
62            AppError::RateLimit { .. } => Self {
63                retryable: true,
64                alertable: false,
65                security_signal: false,
66                user_fixable: true,
67            },
68        }
69    }
70
71    /// Returns `true` if the operation may succeed on a subsequent attempt.
72    #[must_use]
73    pub const fn is_retryable(&self) -> bool {
74        self.retryable
75    }
76
77    /// Returns `true` if this error should trigger an operational alert.
78    #[must_use]
79    pub const fn is_alertable(&self) -> bool {
80        self.alertable
81    }
82
83    /// Returns `true` if this error represents a potential security incident.
84    #[must_use]
85    pub const fn is_security_signal(&self) -> bool {
86        self.security_signal
87    }
88
89    /// Returns `true` if the caller can fix this error without operator intervention.
90    #[must_use]
91    pub const fn is_user_fixable(&self) -> bool {
92        self.user_fixable
93    }
94}