Skip to main content

mailsis_utils/handlers/
reject.rs

1//! Void message handler that always refuses delivery.
2//!
3//! Provides [`RejectHandler`], a [`MessageHandler`](crate::MessageHandler)
4//! implementation that never stores or forwards a message and instead
5//! returns a [`HandlerError::Rejected`] carrying a configurable SMTP reply.
6
7use tracing::{info, warn};
8
9use crate::{
10    handler::{HandlerError, HandlerFuture, MessageHandler},
11    EmailMessage,
12};
13
14/// Message handler that always refuses delivery.
15pub struct RejectHandler {
16    code: u16,
17    message: String,
18}
19
20impl RejectHandler {
21    /// Creates a new [`RejectHandler`] with the given reply code and message.
22    pub fn new(code: u16, message: String) -> Self {
23        info!(
24            code = code,
25            message = %message,
26            "Reject handler initialized"
27        );
28        Self { code, message }
29    }
30
31    /// Returns the configured SMTP reply code.
32    pub fn code(&self) -> u16 {
33        self.code
34    }
35
36    /// Returns the configured SMTP reply message.
37    pub fn message(&self) -> &str {
38        &self.message
39    }
40}
41
42impl MessageHandler for RejectHandler {
43    fn handle<'a>(&'a self, message: &'a EmailMessage) -> HandlerFuture<'a> {
44        Box::pin(async move {
45            warn!(
46                message_id = %message.message_id,
47                to = %message.to,
48                code = self.code,
49                "Rejecting message"
50            );
51            Err(HandlerError::Rejected(self.message.clone()))
52        })
53    }
54
55    fn name(&self) -> &str {
56        "reject"
57    }
58
59    fn reject_reply(&self) -> Option<(u16, String)> {
60        Some((self.code, self.message.clone()))
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[tokio::test]
69    async fn test_reject_handler_returns_rejected_error() {
70        let handler = RejectHandler::new(550, "Relay access denied".to_string());
71        let message = EmailMessage::from_raw("sender@example.com", "rcpt@example.com", "Hi");
72
73        let result = handler.handle(&message).await;
74        match result {
75            Err(HandlerError::Rejected(msg)) => {
76                assert_eq!(msg, "Relay access denied");
77            }
78            other => panic!("Expected Rejected error, got {other:?}"),
79        }
80        assert_eq!(
81            handler.reject_reply(),
82            Some((550, "Relay access denied".to_string()))
83        );
84    }
85
86    #[test]
87    fn test_reject_handler_name() {
88        let handler = RejectHandler::new(550, "Relay access denied".to_string());
89        assert_eq!(handler.name(), "reject");
90    }
91
92    #[test]
93    fn test_reject_handler_accessors() {
94        let handler = RejectHandler::new(521, "No mail accepted here".to_string());
95        assert_eq!(handler.code(), 521);
96        assert_eq!(handler.message(), "No mail accepted here");
97    }
98}