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}