mailsis_utils/handlers/
file_storage.rs1use std::{path::PathBuf, sync::Arc};
8
9use tracing::{debug, error, info};
10
11use crate::{
12 handler::{HandlerError, HandlerFuture, MessageHandler},
13 EmailMessage, FileStorageEngine, StorageEngine,
14};
15
16pub struct FileStorageHandler {
18 engine: Arc<FileStorageEngine>,
19}
20
21impl FileStorageHandler {
22 pub fn new(base_path: PathBuf, metadata: bool) -> Self {
24 info!(
25 path = %base_path.display(),
26 metadata = metadata,
27 "File storage handler initialized"
28 );
29 let engine = if metadata {
30 FileStorageEngine::new(base_path)
31 } else {
32 FileStorageEngine::without_metadata(base_path)
33 };
34 Self {
35 engine: Arc::new(engine),
36 }
37 }
38
39 pub fn engine(&self) -> &Arc<FileStorageEngine> {
41 &self.engine
42 }
43}
44
45impl MessageHandler for FileStorageHandler {
46 fn handle<'a>(&'a self, message: &'a EmailMessage) -> HandlerFuture<'a> {
47 Box::pin(async move {
48 debug!(
49 message_id = %message.message_id,
50 to = %message.to,
51 "Storing email to filesystem"
52 );
53 self.engine.store(message).await.map_err(|e| {
54 error!(
55 message_id = %message.message_id,
56 error = %e,
57 "Failed to store email to filesystem"
58 );
59 HandlerError::Storage(e.to_string())
60 })?;
61 info!(
62 message_id = %message.message_id,
63 from = %message.from,
64 to = %message.to,
65 "Stored email to filesystem"
66 );
67 Ok(())
68 })
69 }
70
71 fn name(&self) -> &str {
72 "file_storage"
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use tempfile::TempDir;
79
80 use super::*;
81
82 #[tokio::test]
83 async fn test_file_storage_handler() {
84 let temp_dir = TempDir::new().unwrap();
85 let handler = FileStorageHandler::new(temp_dir.path().to_path_buf(), false);
86
87 let message = EmailMessage::from_raw("sender@example.com", "rcpt@example.com", "Hello");
88 let result = handler.handle(&message).await;
89 assert!(result.is_ok());
90
91 let messages = handler.engine.list("rcpt@example.com").await.unwrap();
93 assert_eq!(messages.len(), 1);
94 }
95
96 #[test]
97 fn test_file_storage_handler_name() {
98 let temp_dir = TempDir::new().unwrap();
99 let handler = FileStorageHandler::new(temp_dir.path().to_path_buf(), false);
100 assert_eq!(handler.name(), "file_storage");
101 }
102
103 #[test]
104 fn test_file_storage_handler_engine_accessor() {
105 let temp_dir = TempDir::new().unwrap();
106 let handler = FileStorageHandler::new(temp_dir.path().to_path_buf(), false);
107 let _engine = handler.engine();
108 }
109
110 #[tokio::test]
111 async fn test_file_storage_handler_multiple_messages() {
112 let temp_dir = TempDir::new().unwrap();
113 let handler = FileStorageHandler::new(temp_dir.path().to_path_buf(), false);
114
115 let msg1 = EmailMessage::from_raw("sender@example.com", "rcpt@example.com", "Message 1");
116 let msg2 = EmailMessage::from_raw("sender@example.com", "rcpt@example.com", "Message 2");
117
118 handler.handle(&msg1).await.unwrap();
119 handler.handle(&msg2).await.unwrap();
120
121 let messages = handler.engine.list("rcpt@example.com").await.unwrap();
122 assert_eq!(messages.len(), 2);
123 }
124
125 #[tokio::test]
126 async fn test_file_storage_handler_with_mime_message() {
127 let temp_dir = TempDir::new().unwrap();
128 let handler = FileStorageHandler::new(temp_dir.path().to_path_buf(), false);
129
130 let message = EmailMessage::from_raw(
131 "sender@example.com",
132 "rcpt@example.com",
133 "Subject: Test\r\nFrom: sender@example.com\r\n\r\nBody content",
134 );
135
136 handler.handle(&message).await.unwrap();
137
138 let content = handler
139 .engine
140 .retrieve("rcpt@example.com", &message.message_id)
141 .await
142 .unwrap();
143 assert!(content.contains("Subject: Test"));
144 assert!(content.contains("Body content"));
145 }
146}