Skip to main content

mailsis_utils/
handler.rs

1//! Delivery backend abstraction for incoming email messages.
2//!
3//! Every destination that can receive an email - filesystem storage, a Redis
4//! queue, etc. - implements the [`MessageHandler`] trait defined here.
5//! The trait is object-safe so that the router can dispatch dynamically.
6
7use std::{error::Error, fmt::Display, future::Future, pin::Pin};
8
9/// Result type for handler operations.
10pub type HandlerResult<T> = Result<T, HandlerError>;
11
12/// Boxed future type for handler operations, enabling object safety.
13pub type HandlerFuture<'a> = Pin<Box<dyn Future<Output = HandlerResult<()>> + Send + 'a>>;
14
15/// Errors that can occur during message handling.
16#[derive(Debug)]
17pub enum HandlerError {
18    /// A storage error occurred.
19    Storage(String),
20    /// A connection error occurred.
21    Connection(String),
22    /// A serialization error occurred.
23    Serialization(String),
24    /// The recipient was refused by a reject handler.
25    Rejected(String),
26}
27
28impl Display for HandlerError {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        match self {
31            HandlerError::Storage(msg) => write!(f, "Storage error: {msg}"),
32            HandlerError::Connection(msg) => write!(f, "Connection error: {msg}"),
33            HandlerError::Serialization(msg) => write!(f, "Serialization error: {msg}"),
34            HandlerError::Rejected(msg) => write!(f, "Rejected: {msg}"),
35        }
36    }
37}
38
39impl Error for HandlerError {}
40
41/// Trait for message handlers that process incoming emails.
42///
43/// Unlike [`StorageEngine`], this trait only handles the inbound direction
44/// (receiving/storing), making it suitable for both storage backends
45/// and queue-based destinations like Redis.
46pub trait MessageHandler: Send + Sync {
47    /// Handles an incoming email message.
48    fn handle<'a>(&'a self, message: &'a crate::EmailMessage) -> HandlerFuture<'a>;
49
50    /// Returns the name of this handler.
51    fn name(&self) -> &str;
52
53    /// Returns the SMTP reply `(code, message)` for handlers that refuse
54    /// every delivery, or [`None`] for handlers that accept messages.
55    fn reject_reply(&self) -> Option<(u16, String)> {
56        None
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn test_handler_error_display() {
66        assert_eq!(
67            HandlerError::Storage("test".to_string()).to_string(),
68            "Storage error: test"
69        );
70        assert_eq!(
71            HandlerError::Connection("test".to_string()).to_string(),
72            "Connection error: test"
73        );
74        assert_eq!(
75            HandlerError::Serialization("test".to_string()).to_string(),
76            "Serialization error: test"
77        );
78        assert_eq!(
79            HandlerError::Rejected("Relay access denied".to_string()).to_string(),
80            "Rejected: Relay access denied"
81        );
82    }
83}