Skip to main content

rusmes_core/
dsn.rs

1//! DSN (Delivery Status Notification) utilities - RFC 3464 and RFC 3463
2//!
3//! This module provides utilities for generating RFC-compliant DSN messages
4//! and handling enhanced status codes.
5
6use std::fmt;
7
8/// Enhanced Status Code (RFC 3463)
9///
10/// Format: X.Y.Z where:
11/// - X: Class (2=Success, 4=Transient Failure, 5=Permanent Failure)
12/// - Y: Subject (0=Other, 1=Addressing, 2=Mailbox, 3=Mail System, 4=Network, 5=Protocol, 6=Content, 7=Security)
13/// - Z: Detail (specific detail code)
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct EnhancedStatusCode {
16    pub class: u8,
17    pub subject: u8,
18    pub detail: u8,
19}
20
21impl EnhancedStatusCode {
22    /// Create a new enhanced status code
23    pub const fn new(class: u8, subject: u8, detail: u8) -> Self {
24        Self {
25            class,
26            subject,
27            detail,
28        }
29    }
30
31    /// Parse enhanced status code from string (e.g., "5.1.1")
32    pub fn parse(s: &str) -> Option<Self> {
33        let parts: Vec<&str> = s.split('.').collect();
34        if parts.len() != 3 {
35            return None;
36        }
37
38        let class = parts[0].parse().ok()?;
39        let subject = parts[1].parse().ok()?;
40        let detail = parts[2].parse().ok()?;
41
42        Some(Self::new(class, subject, detail))
43    }
44
45    /// Check if this is a success code (2.X.X)
46    pub fn is_success(&self) -> bool {
47        self.class == 2
48    }
49
50    /// Check if this is a transient failure (4.X.X)
51    pub fn is_transient(&self) -> bool {
52        self.class == 4
53    }
54
55    /// Check if this is a permanent failure (5.X.X)
56    pub fn is_permanent(&self) -> bool {
57        self.class == 5
58    }
59}
60
61impl fmt::Display for EnhancedStatusCode {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        write!(f, "{}.{}.{}", self.class, self.subject, self.detail)
64    }
65}
66
67/// Common enhanced status codes (RFC 3463)
68#[allow(dead_code)]
69impl EnhancedStatusCode {
70    // Success codes (2.X.X)
71    pub const SUCCESS: Self = Self::new(2, 0, 0);
72
73    // Addressing status (X.1.X)
74    pub const BAD_DESTINATION_MAILBOX: Self = Self::new(5, 1, 1); // 5.1.1 - User unknown
75    pub const BAD_DESTINATION_SYSTEM: Self = Self::new(5, 1, 2); // 5.1.2 - System not accepting mail
76    pub const BAD_DESTINATION_SYNTAX: Self = Self::new(5, 1, 3); // 5.1.3 - Invalid address syntax
77    pub const DESTINATION_AMBIGUOUS: Self = Self::new(5, 1, 4); // 5.1.4 - Ambiguous address
78    pub const DESTINATION_VALID: Self = Self::new(2, 1, 5); // 2.1.5 - Destination valid
79    pub const MAILBOX_MOVED: Self = Self::new(5, 1, 6); // 5.1.6 - Mailbox moved
80    pub const BAD_SENDER_ADDRESS: Self = Self::new(5, 1, 7); // 5.1.7 - Bad sender address
81    pub const BAD_SENDER_SYSTEM: Self = Self::new(5, 1, 8); // 5.1.8 - Bad sender system
82
83    // Mailbox status (X.2.X)
84    pub const MAILBOX_DISABLED: Self = Self::new(5, 2, 1); // 5.2.1 - Mailbox disabled
85    pub const MAILBOX_FULL: Self = Self::new(5, 2, 2); // 5.2.2 - Mailbox full
86    pub const MAILBOX_FULL_TEMP: Self = Self::new(4, 2, 2); // 4.2.2 - Mailbox full (temporary)
87    pub const MESSAGE_TOO_LARGE: Self = Self::new(5, 2, 3); // 5.2.3 - Message too large
88    pub const MAILING_LIST_EXPANSION: Self = Self::new(5, 2, 4); // 5.2.4 - Mailing list expansion problem
89
90    // Mail system status (X.3.X)
91    pub const SYSTEM_FULL: Self = Self::new(4, 3, 1); // 4.3.1 - System full
92    pub const SYSTEM_NOT_ACCEPTING: Self = Self::new(4, 3, 2); // 4.3.2 - System not accepting messages
93    pub const SYSTEM_CAPABILITY: Self = Self::new(5, 3, 3); // 5.3.3 - System capability not supported
94    pub const MESSAGE_TOO_BIG: Self = Self::new(5, 3, 4); // 5.3.4 - Message too big for system
95    pub const SYSTEM_INCORRECTLY_CONFIGURED: Self = Self::new(5, 3, 5); // 5.3.5 - System incorrectly configured
96
97    // Network and routing status (X.4.X)
98    pub const NO_ANSWER: Self = Self::new(4, 4, 1); // 4.4.1 - No answer from host
99    pub const CONNECTION_DROPPED: Self = Self::new(4, 4, 2); // 4.4.2 - Connection dropped
100    pub const ROUTING_SERVER_FAILURE: Self = Self::new(4, 4, 3); // 4.4.3 - Routing server failure
101    pub const NETWORK_CONGESTION: Self = Self::new(4, 4, 5); // 4.4.5 - Network congestion
102    pub const ROUTING_LOOP: Self = Self::new(5, 4, 6); // 5.4.6 - Routing loop detected
103    pub const DELIVERY_TIME_EXPIRED: Self = Self::new(4, 4, 7); // 4.4.7 - Delivery time expired
104
105    // Mail delivery protocol status (X.5.X)
106    pub const INVALID_COMMAND: Self = Self::new(5, 5, 1); // 5.5.1 - Invalid command
107    pub const SYNTAX_ERROR: Self = Self::new(5, 5, 2); // 5.5.2 - Syntax error
108    pub const TOO_MANY_RECIPIENTS: Self = Self::new(5, 5, 3); // 5.5.3 - Too many recipients
109    pub const INVALID_PARAMETERS: Self = Self::new(5, 5, 4); // 5.5.4 - Invalid command arguments
110    pub const WRONG_PROTOCOL: Self = Self::new(5, 5, 5); // 5.5.5 - Wrong protocol version
111
112    // Content/media status (X.6.X)
113    pub const MEDIA_NOT_SUPPORTED: Self = Self::new(5, 6, 1); // 5.6.1 - Media not supported
114    pub const CONVERSION_REQUIRED: Self = Self::new(5, 6, 2); // 5.6.2 - Conversion required
115    pub const CONVERSION_NOT_POSSIBLE: Self = Self::new(5, 6, 3); // 5.6.3 - Conversion not possible
116    pub const CONVERSION_LOST: Self = Self::new(5, 6, 4); // 5.6.4 - Conversion with loss
117    pub const CONVERSION_FAILED: Self = Self::new(5, 6, 5); // 5.6.5 - Conversion failed
118
119    // Security/policy status (X.7.X)
120    pub const DELIVERY_NOT_AUTHORIZED: Self = Self::new(5, 7, 1); // 5.7.1 - Delivery not authorized
121    pub const MAILING_LIST_EXPANSION_PROHIBITED: Self = Self::new(5, 7, 2); // 5.7.2 - Mailing list expansion prohibited
122    pub const SECURITY_CONVERSION_REQUIRED: Self = Self::new(5, 7, 3); // 5.7.3 - Security conversion required
123    pub const SECURITY_FEATURES_NOT_SUPPORTED: Self = Self::new(5, 7, 4); // 5.7.4 - Security features not supported
124    pub const CRYPTOGRAPHIC_FAILURE: Self = Self::new(5, 7, 5); // 5.7.5 - Cryptographic failure
125    pub const CRYPTOGRAPHIC_ALGORITHM_NOT_SUPPORTED: Self = Self::new(5, 7, 6); // 5.7.6 - Cryptographic algorithm not supported
126    pub const MESSAGE_INTEGRITY_FAILURE: Self = Self::new(5, 7, 7); // 5.7.7 - Message integrity failure
127    pub const AUTHENTICATION_CREDENTIALS_INVALID: Self = Self::new(5, 7, 8); // 5.7.8 - Authentication credentials invalid
128    pub const AUTHENTICATION_MECHANISM_TOO_WEAK: Self = Self::new(5, 7, 9); // 5.7.9 - Authentication mechanism too weak
129    pub const ENCRYPTION_NEEDED: Self = Self::new(5, 7, 11); // 5.7.11 - Encryption needed
130    pub const SENDER_ADDRESS_INVALID: Self = Self::new(5, 7, 12); // 5.7.12 - Sender address has null MX
131    pub const MESSAGE_REFUSED: Self = Self::new(5, 7, 13); // 5.7.13 - Message refused
132    pub const TRUST_RELATIONSHIP_REQUIRED: Self = Self::new(5, 7, 14); // 5.7.14 - Trust relationship required
133    pub const PRIORITY_TOO_LOW: Self = Self::new(5, 7, 15); // 5.7.15 - Priority too low
134    pub const MESSAGE_TOO_BIG_FOR_POLICY: Self = Self::new(5, 7, 17); // 5.7.17 - Message too big
135    pub const MAILBOX_OWNER_CHANGED: Self = Self::new(5, 7, 18); // 5.7.18 - Mailbox owner has changed
136    pub const RRVS_CANNOT_VALIDATE: Self = Self::new(5, 7, 19); // 5.7.19 - RRVS cannot validate
137}
138
139/// Delivery failure reasons
140#[derive(Debug, Clone, Copy, PartialEq, Eq)]
141pub enum FailureReason {
142    /// User unknown / Mailbox does not exist
143    UserUnknown,
144    /// Quota exceeded
145    QuotaExceeded,
146    /// Message too large
147    MessageTooLarge,
148    /// Content rejected (spam, virus)
149    ContentRejected,
150    /// Relay denied
151    RelayDenied,
152    /// Temporary failure (try again later)
153    TemporaryFailure,
154    /// Network unreachable
155    NetworkUnreachable,
156    /// Connection timeout
157    ConnectionTimeout,
158    /// Invalid address
159    InvalidAddress,
160    /// Mailbox disabled
161    MailboxDisabled,
162    /// System not accepting mail
163    SystemNotAccepting,
164    /// Authentication required
165    AuthenticationRequired,
166    /// Spam detected
167    SpamDetected,
168    /// Virus detected
169    VirusDetected,
170    /// Other/unknown error
171    Other,
172}
173
174impl FailureReason {
175    /// Get the enhanced status code for this failure reason
176    pub fn enhanced_code(&self) -> EnhancedStatusCode {
177        match self {
178            Self::UserUnknown => EnhancedStatusCode::BAD_DESTINATION_MAILBOX,
179            Self::QuotaExceeded => EnhancedStatusCode::MAILBOX_FULL,
180            Self::MessageTooLarge => EnhancedStatusCode::MESSAGE_TOO_LARGE,
181            Self::ContentRejected => EnhancedStatusCode::MESSAGE_REFUSED,
182            Self::RelayDenied => EnhancedStatusCode::DELIVERY_NOT_AUTHORIZED,
183            Self::TemporaryFailure => EnhancedStatusCode::new(4, 0, 0),
184            Self::NetworkUnreachable => EnhancedStatusCode::NO_ANSWER,
185            Self::ConnectionTimeout => EnhancedStatusCode::CONNECTION_DROPPED,
186            Self::InvalidAddress => EnhancedStatusCode::BAD_DESTINATION_SYNTAX,
187            Self::MailboxDisabled => EnhancedStatusCode::MAILBOX_DISABLED,
188            Self::SystemNotAccepting => EnhancedStatusCode::SYSTEM_NOT_ACCEPTING,
189            Self::AuthenticationRequired => EnhancedStatusCode::DELIVERY_NOT_AUTHORIZED,
190            Self::SpamDetected => EnhancedStatusCode::MESSAGE_REFUSED,
191            Self::VirusDetected => EnhancedStatusCode::MESSAGE_REFUSED,
192            Self::Other => EnhancedStatusCode::new(5, 0, 0),
193        }
194    }
195
196    /// Get a human-readable description
197    pub fn description(&self) -> &'static str {
198        match self {
199            Self::UserUnknown => "The recipient's email address does not exist",
200            Self::QuotaExceeded => "The recipient's mailbox is full",
201            Self::MessageTooLarge => "The message is too large to be delivered",
202            Self::ContentRejected => "The message content was rejected by policy",
203            Self::RelayDenied => "Relay access denied",
204            Self::TemporaryFailure => "Temporary failure, will retry delivery",
205            Self::NetworkUnreachable => "The destination mail server could not be reached",
206            Self::ConnectionTimeout => "Connection to the mail server timed out",
207            Self::InvalidAddress => "The recipient's email address is invalid",
208            Self::MailboxDisabled => "The recipient's mailbox is disabled",
209            Self::SystemNotAccepting => "The mail system is not accepting messages",
210            Self::AuthenticationRequired => "Authentication is required for this delivery",
211            Self::SpamDetected => "The message was identified as spam",
212            Self::VirusDetected => "The message contains a virus",
213            Self::Other => "An unknown error occurred",
214        }
215    }
216
217    /// Check if this is a permanent failure
218    pub fn is_permanent(&self) -> bool {
219        matches!(
220            self,
221            Self::UserUnknown
222                | Self::QuotaExceeded
223                | Self::MessageTooLarge
224                | Self::ContentRejected
225                | Self::RelayDenied
226                | Self::InvalidAddress
227                | Self::MailboxDisabled
228                | Self::SpamDetected
229                | Self::VirusDetected
230        )
231    }
232
233    /// Convert from SMTP status code
234    pub fn from_smtp_code(code: u16) -> Self {
235        match code {
236            421 => Self::ConnectionTimeout,
237            450 => Self::TemporaryFailure,
238            451 => Self::TemporaryFailure,
239            452 => Self::QuotaExceeded,
240            550 => Self::UserUnknown,
241            551 => Self::RelayDenied,
242            552 => Self::QuotaExceeded,
243            553 => Self::InvalidAddress,
244            554 => Self::ContentRejected,
245            _ if (400..500).contains(&code) => Self::TemporaryFailure,
246            _ => Self::Other,
247        }
248    }
249}
250
251impl fmt::Display for FailureReason {
252    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253        write!(f, "{}", self.description())
254    }
255}
256
257/// Map SMTP status code to enhanced status code
258pub fn smtp_to_enhanced_code(smtp_code: u16) -> EnhancedStatusCode {
259    match smtp_code {
260        // 2xx - Success
261        250 => EnhancedStatusCode::SUCCESS,
262        251 => EnhancedStatusCode::DESTINATION_VALID,
263
264        // 4xx - Transient failures
265        421 => EnhancedStatusCode::CONNECTION_DROPPED,
266        450 => EnhancedStatusCode::new(4, 2, 1), // Mailbox unavailable
267        451 => EnhancedStatusCode::new(4, 3, 0), // Local error in processing
268        452 => EnhancedStatusCode::MAILBOX_FULL_TEMP,
269        454 => EnhancedStatusCode::new(4, 7, 0), // Temporary authentication failure
270
271        // 5xx - Permanent failures
272        500 => EnhancedStatusCode::SYNTAX_ERROR,
273        501 => EnhancedStatusCode::INVALID_PARAMETERS,
274        502 => EnhancedStatusCode::INVALID_COMMAND,
275        503 => EnhancedStatusCode::INVALID_COMMAND,
276        504 => EnhancedStatusCode::INVALID_PARAMETERS,
277        550 => EnhancedStatusCode::BAD_DESTINATION_MAILBOX,
278        551 => EnhancedStatusCode::MAILBOX_MOVED,
279        552 => EnhancedStatusCode::MAILBOX_FULL,
280        553 => EnhancedStatusCode::BAD_DESTINATION_SYNTAX,
281        554 => EnhancedStatusCode::DELIVERY_NOT_AUTHORIZED,
282
283        // Default cases
284        _ if (200..300).contains(&smtp_code) => EnhancedStatusCode::SUCCESS,
285        _ if (400..500).contains(&smtp_code) => EnhancedStatusCode::new(4, 0, 0),
286        _ if (500..600).contains(&smtp_code) => EnhancedStatusCode::new(5, 0, 0),
287        _ => EnhancedStatusCode::new(5, 0, 0),
288    }
289}
290
291/// Get diagnostic text for SMTP code
292pub fn smtp_diagnostic_text(smtp_code: u16) -> &'static str {
293    match smtp_code {
294        421 => "Service not available, closing transmission channel",
295        450 => "Requested mail action not taken: mailbox unavailable",
296        451 => "Requested action aborted: local error in processing",
297        452 => "Requested action not taken: insufficient system storage",
298        454 => "Temporary authentication failure",
299        500 => "Syntax error, command unrecognized",
300        501 => "Syntax error in parameters or arguments",
301        502 => "Command not implemented",
302        503 => "Bad sequence of commands",
303        504 => "Command parameter not implemented",
304        550 => "Requested action not taken: mailbox unavailable",
305        551 => "User not local; please try forward path",
306        552 => "Requested mail action aborted: exceeded storage allocation",
307        553 => "Requested action not taken: mailbox name not allowed",
308        554 => "Transaction failed",
309        _ => "Unknown error",
310    }
311}
312
313#[cfg(test)]
314mod tests {
315    use super::*;
316
317    #[test]
318    fn test_enhanced_status_code_display() {
319        let code = EnhancedStatusCode::new(5, 1, 1);
320        assert_eq!(code.to_string(), "5.1.1");
321    }
322
323    #[test]
324    fn test_enhanced_status_code_parse() {
325        let code = EnhancedStatusCode::parse("5.1.1").unwrap();
326        assert_eq!(code.class, 5);
327        assert_eq!(code.subject, 1);
328        assert_eq!(code.detail, 1);
329    }
330
331    #[test]
332    fn test_enhanced_status_code_parse_invalid() {
333        assert!(EnhancedStatusCode::parse("5.1").is_none());
334        assert!(EnhancedStatusCode::parse("5.1.1.1").is_none());
335        assert!(EnhancedStatusCode::parse("abc").is_none());
336    }
337
338    #[test]
339    fn test_enhanced_status_code_is_success() {
340        assert!(EnhancedStatusCode::new(2, 0, 0).is_success());
341        assert!(!EnhancedStatusCode::new(4, 0, 0).is_success());
342        assert!(!EnhancedStatusCode::new(5, 0, 0).is_success());
343    }
344
345    #[test]
346    fn test_enhanced_status_code_is_transient() {
347        assert!(!EnhancedStatusCode::new(2, 0, 0).is_transient());
348        assert!(EnhancedStatusCode::new(4, 0, 0).is_transient());
349        assert!(!EnhancedStatusCode::new(5, 0, 0).is_transient());
350    }
351
352    #[test]
353    fn test_enhanced_status_code_is_permanent() {
354        assert!(!EnhancedStatusCode::new(2, 0, 0).is_permanent());
355        assert!(!EnhancedStatusCode::new(4, 0, 0).is_permanent());
356        assert!(EnhancedStatusCode::new(5, 0, 0).is_permanent());
357    }
358
359    #[test]
360    fn test_failure_reason_enhanced_code() {
361        assert_eq!(
362            FailureReason::UserUnknown.enhanced_code(),
363            EnhancedStatusCode::BAD_DESTINATION_MAILBOX
364        );
365        assert_eq!(
366            FailureReason::QuotaExceeded.enhanced_code(),
367            EnhancedStatusCode::MAILBOX_FULL
368        );
369        assert_eq!(
370            FailureReason::MessageTooLarge.enhanced_code(),
371            EnhancedStatusCode::MESSAGE_TOO_LARGE
372        );
373    }
374
375    #[test]
376    fn test_failure_reason_is_permanent() {
377        assert!(FailureReason::UserUnknown.is_permanent());
378        assert!(!FailureReason::TemporaryFailure.is_permanent());
379        assert!(FailureReason::InvalidAddress.is_permanent());
380    }
381
382    #[test]
383    fn test_failure_reason_from_smtp_code() {
384        assert_eq!(
385            FailureReason::from_smtp_code(550),
386            FailureReason::UserUnknown
387        );
388        assert_eq!(
389            FailureReason::from_smtp_code(452),
390            FailureReason::QuotaExceeded
391        );
392        assert_eq!(
393            FailureReason::from_smtp_code(421),
394            FailureReason::ConnectionTimeout
395        );
396    }
397
398    #[test]
399    fn test_smtp_to_enhanced_code_421() {
400        assert_eq!(
401            smtp_to_enhanced_code(421),
402            EnhancedStatusCode::CONNECTION_DROPPED
403        );
404    }
405
406    #[test]
407    fn test_smtp_to_enhanced_code_450() {
408        let code = smtp_to_enhanced_code(450);
409        assert_eq!(code.class, 4);
410        assert_eq!(code.subject, 2);
411        assert_eq!(code.detail, 1);
412    }
413
414    #[test]
415    fn test_smtp_to_enhanced_code_451() {
416        let code = smtp_to_enhanced_code(451);
417        assert_eq!(code.class, 4);
418        assert_eq!(code.subject, 3);
419        assert_eq!(code.detail, 0);
420    }
421
422    #[test]
423    fn test_smtp_to_enhanced_code_452() {
424        assert_eq!(
425            smtp_to_enhanced_code(452),
426            EnhancedStatusCode::MAILBOX_FULL_TEMP
427        );
428    }
429
430    #[test]
431    fn test_smtp_to_enhanced_code_550() {
432        assert_eq!(
433            smtp_to_enhanced_code(550),
434            EnhancedStatusCode::BAD_DESTINATION_MAILBOX
435        );
436    }
437
438    #[test]
439    fn test_smtp_to_enhanced_code_551() {
440        assert_eq!(
441            smtp_to_enhanced_code(551),
442            EnhancedStatusCode::MAILBOX_MOVED
443        );
444    }
445
446    #[test]
447    fn test_smtp_to_enhanced_code_552() {
448        assert_eq!(smtp_to_enhanced_code(552), EnhancedStatusCode::MAILBOX_FULL);
449    }
450
451    #[test]
452    fn test_smtp_to_enhanced_code_553() {
453        assert_eq!(
454            smtp_to_enhanced_code(553),
455            EnhancedStatusCode::BAD_DESTINATION_SYNTAX
456        );
457    }
458
459    #[test]
460    fn test_smtp_to_enhanced_code_554() {
461        assert_eq!(
462            smtp_to_enhanced_code(554),
463            EnhancedStatusCode::DELIVERY_NOT_AUTHORIZED
464        );
465    }
466
467    #[test]
468    fn test_smtp_diagnostic_text() {
469        assert_eq!(
470            smtp_diagnostic_text(421),
471            "Service not available, closing transmission channel"
472        );
473        assert_eq!(
474            smtp_diagnostic_text(550),
475            "Requested action not taken: mailbox unavailable"
476        );
477    }
478
479    #[test]
480    fn test_smtp_diagnostic_text_unknown() {
481        assert_eq!(smtp_diagnostic_text(999), "Unknown error");
482    }
483
484    #[test]
485    fn test_enhanced_code_constants() {
486        assert_eq!(EnhancedStatusCode::SUCCESS.to_string(), "2.0.0");
487        assert_eq!(
488            EnhancedStatusCode::BAD_DESTINATION_MAILBOX.to_string(),
489            "5.1.1"
490        );
491        assert_eq!(EnhancedStatusCode::MAILBOX_FULL.to_string(), "5.2.2");
492    }
493
494    #[test]
495    fn test_failure_reason_description() {
496        assert!(!FailureReason::UserUnknown.description().is_empty());
497        assert!(!FailureReason::QuotaExceeded.description().is_empty());
498    }
499
500    #[test]
501    fn test_failure_reason_display() {
502        let reason = FailureReason::UserUnknown;
503        assert_eq!(reason.to_string(), reason.description());
504    }
505
506    #[test]
507    fn test_smtp_to_enhanced_code_success_range() {
508        let code = smtp_to_enhanced_code(250);
509        assert_eq!(code, EnhancedStatusCode::SUCCESS);
510    }
511
512    #[test]
513    fn test_smtp_to_enhanced_code_temp_failure_range() {
514        let code = smtp_to_enhanced_code(499);
515        assert!(code.is_transient());
516    }
517
518    #[test]
519    fn test_smtp_to_enhanced_code_perm_failure_range() {
520        let code = smtp_to_enhanced_code(599);
521        assert!(code.is_permanent());
522    }
523
524    #[test]
525    fn test_all_failure_reasons() {
526        // Test all variants exist and work
527        let reasons = [
528            FailureReason::UserUnknown,
529            FailureReason::QuotaExceeded,
530            FailureReason::MessageTooLarge,
531            FailureReason::ContentRejected,
532            FailureReason::RelayDenied,
533            FailureReason::TemporaryFailure,
534            FailureReason::NetworkUnreachable,
535            FailureReason::ConnectionTimeout,
536            FailureReason::InvalidAddress,
537            FailureReason::MailboxDisabled,
538            FailureReason::SystemNotAccepting,
539            FailureReason::AuthenticationRequired,
540            FailureReason::SpamDetected,
541            FailureReason::VirusDetected,
542            FailureReason::Other,
543        ];
544
545        for reason in &reasons {
546            let _ = reason.enhanced_code();
547            let _ = reason.description();
548            let _ = reason.is_permanent();
549        }
550    }
551}