1use std::{collections::HashMap, error::Error, fmt::Display, io, path::PathBuf, sync::RwLock};
8
9use chrono::Utc;
10use tokio::{
11 fs::{self, File},
12 io::AsyncWriteExt,
13};
14
15use crate::{EmailMessage, EmailMetadata};
16
17pub type StorageResult<T> = Result<T, StorageError>;
19
20#[derive(Debug)]
22pub enum StorageError {
23 NotFound,
25 Io(io::Error),
27 EngineError(String),
29}
30
31impl Display for StorageError {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 match self {
34 StorageError::NotFound => write!(f, "Message not found"),
35 StorageError::Io(error) => write!(f, "I/O error: {error}"),
36 StorageError::EngineError(msg) => write!(f, "Storage error: {msg}"),
37 }
38 }
39}
40
41impl Error for StorageError {}
42
43impl From<io::Error> for StorageError {
44 fn from(error: io::Error) -> Self {
45 if error.kind() == io::ErrorKind::NotFound {
46 StorageError::NotFound
47 } else {
48 StorageError::Io(error)
49 }
50 }
51}
52
53pub trait StorageEngine: Send + Sync {
58 fn store(
62 &self,
63 message: &EmailMessage,
64 ) -> impl std::future::Future<Output = StorageResult<String>> + Send;
65
66 fn retrieve(
68 &self,
69 user: &str,
70 message_id: &str,
71 ) -> impl std::future::Future<Output = StorageResult<String>> + Send;
72
73 fn list(
75 &self,
76 user: &str,
77 ) -> impl std::future::Future<Output = StorageResult<Vec<String>>> + Send;
78
79 fn delete(
81 &self,
82 user: &str,
83 message_id: &str,
84 ) -> impl std::future::Future<Output = StorageResult<()>> + Send;
85
86 fn create_mailbox(
88 &self,
89 user: &str,
90 mailbox: &str,
91 ) -> impl std::future::Future<Output = StorageResult<()>> + Send;
92}
93
94#[derive(Debug, Clone)]
99pub struct FileStorageEngine {
100 base_path: PathBuf,
102
103 db_path: PathBuf,
105
106 store_metadata: bool,
108}
109
110impl FileStorageEngine {
111 pub fn new(base_path: PathBuf) -> Self {
113 let db_path = base_path.join("metadata.db");
114 Self {
115 base_path,
116 db_path,
117 store_metadata: true,
118 }
119 }
120
121 pub fn without_metadata(base_path: PathBuf) -> Self {
123 Self {
124 base_path,
125 db_path: PathBuf::new(),
126 store_metadata: false,
127 }
128 }
129
130 fn user_path(&self, user: &str) -> PathBuf {
132 let safe_user = user.replace(|c: char| !c.is_ascii_alphanumeric(), "_");
133 self.base_path.join(safe_user)
134 }
135
136 fn message_path(&self, user: &str, message_id: &str) -> PathBuf {
138 self.user_path(user).join(format!("{message_id}.eml"))
139 }
140}
141
142impl StorageEngine for FileStorageEngine {
143 async fn store(&self, message: &EmailMessage) -> StorageResult<String> {
144 let user_dir = self.user_path(&message.to);
145 fs::create_dir_all(&user_dir).await?;
146
147 let file_path = self.message_path(&message.to, &message.message_id);
148 let mut file = File::create(&file_path).await?;
149
150 if !message.has_headers() {
152 file.write_all(format!("From: {}\r\n", message.from).as_bytes())
153 .await?;
154 file.write_all(format!("To: {}\r\n", message.to).as_bytes())
155 .await?;
156 file.write_all(format!("Date: {}\r\n\r\n", Utc::now().to_rfc2822()).as_bytes())
157 .await?;
158 }
159 file.write_all(message.raw().as_bytes()).await?;
160 file.flush().await?;
161
162 if self.store_metadata {
164 let metadata = EmailMetadata::new(
165 message.message_id.clone(),
166 message.from.clone(),
167 message.to.clone(),
168 message.subject().to_string(),
169 file_path,
170 );
171 metadata
172 .store_sqlite(&self.db_path)
173 .await
174 .map_err(|error| StorageError::EngineError(error.to_string()))?;
175 }
176
177 Ok(message.message_id.clone())
178 }
179
180 async fn retrieve(&self, user: &str, message_id: &str) -> StorageResult<String> {
181 let path = self.message_path(user, message_id);
182 let content = fs::read_to_string(path).await?;
183 Ok(content)
184 }
185
186 async fn list(&self, user: &str) -> StorageResult<Vec<String>> {
187 let user_dir = self.user_path(user);
188
189 if !user_dir.exists() {
191 return Ok(Vec::new());
192 }
193
194 let mut messages = Vec::new();
195 let mut entries = fs::read_dir(&user_dir).await?;
196
197 while let Some(entry) = entries.next_entry().await? {
198 let path = entry.path();
199 if let Some(ext) = path.extension() {
200 if ext == "eml" {
201 if let Some(stem) = path.file_stem() {
202 messages.push(stem.to_string_lossy().to_string());
203 }
204 }
205 }
206 }
207
208 Ok(messages)
209 }
210
211 async fn delete(&self, user: &str, message_id: &str) -> StorageResult<()> {
212 let path = self.message_path(user, message_id);
213 fs::remove_file(path).await?;
214 Ok(())
215 }
216
217 async fn create_mailbox(&self, user: &str, mailbox: &str) -> StorageResult<()> {
218 let path = self.user_path(user).join(format!("{mailbox}.mbox"));
219 fs::create_dir_all(path).await?;
220 Ok(())
221 }
222}
223
224impl Default for FileStorageEngine {
225 fn default() -> Self {
226 Self::new(PathBuf::from("mailbox"))
227 }
228}
229
230#[derive(Debug, Default)]
237pub struct MemoryStorageEngine {
238 messages: RwLock<HashMap<String, HashMap<String, EmailMessage>>>,
240
241 mailboxes: RwLock<HashMap<String, Vec<String>>>,
243}
244
245impl MemoryStorageEngine {
246 pub fn new() -> Self {
248 Self {
249 messages: RwLock::new(HashMap::new()),
250 mailboxes: RwLock::new(HashMap::new()),
251 }
252 }
253
254 pub fn message_count(&self, user: &str) -> usize {
256 let safe_user = Self::safe_user(user);
257 self.messages
258 .read()
259 .unwrap()
260 .get(&safe_user)
261 .map(|m| m.len())
262 .unwrap_or(0)
263 }
264
265 pub fn total_message_count(&self) -> usize {
267 self.messages
268 .read()
269 .unwrap()
270 .values()
271 .map(|m| m.len())
272 .sum()
273 }
274
275 pub fn clear(&self) {
277 self.messages.write().unwrap().clear();
278 self.mailboxes.write().unwrap().clear();
279 }
280
281 fn safe_user(user: &str) -> String {
283 user.replace(|c: char| !c.is_ascii_alphanumeric(), "_")
284 }
285}
286
287impl StorageEngine for MemoryStorageEngine {
288 async fn store(&self, message: &EmailMessage) -> StorageResult<String> {
289 let safe_user = Self::safe_user(&message.to);
290 let mut messages = self.messages.write().unwrap();
291 let user_messages = messages.entry(safe_user).or_default();
292 user_messages.insert(message.message_id.clone(), message.clone());
293 Ok(message.message_id.clone())
294 }
295
296 async fn retrieve(&self, user: &str, message_id: &str) -> StorageResult<String> {
297 let safe_user = Self::safe_user(user);
298 let messages = self.messages.read().unwrap();
299 let user_messages = messages.get(&safe_user).ok_or(StorageError::NotFound)?;
300 let message = user_messages
301 .get(message_id)
302 .ok_or(StorageError::NotFound)?;
303 Ok(message.raw().to_string())
304 }
305
306 async fn list(&self, user: &str) -> StorageResult<Vec<String>> {
307 let safe_user = Self::safe_user(user);
308 let messages = self.messages.read().unwrap();
309 match messages.get(&safe_user) {
310 Some(user_messages) => Ok(user_messages.keys().cloned().collect()),
311 None => Ok(Vec::new()),
312 }
313 }
314
315 async fn delete(&self, user: &str, message_id: &str) -> StorageResult<()> {
316 let safe_user = Self::safe_user(user);
317 let mut messages = self.messages.write().unwrap();
318 let user_messages = messages.get_mut(&safe_user).ok_or(StorageError::NotFound)?;
319 user_messages
320 .remove(message_id)
321 .ok_or(StorageError::NotFound)?;
322 Ok(())
323 }
324
325 async fn create_mailbox(&self, user: &str, mailbox: &str) -> StorageResult<()> {
326 let safe_user = Self::safe_user(user);
327 let mut mailboxes = self.mailboxes.write().unwrap();
328 let user_mailboxes = mailboxes.entry(safe_user).or_default();
329 if !user_mailboxes.contains(&mailbox.to_string()) {
330 user_mailboxes.push(mailbox.to_string());
331 }
332 Ok(())
333 }
334}
335
336#[cfg(test)]
337mod tests {
338 use tempfile::TempDir;
339
340 use super::*;
341
342 #[tokio::test]
343 async fn test_file_storage_store_and_retrieve() {
344 let temp_dir = TempDir::new().unwrap();
345 let engine = FileStorageEngine::without_metadata(temp_dir.path().to_path_buf());
346
347 let message = EmailMessage::new(
348 "sender@example.com".to_string(),
349 "recipient@example.com".to_string(),
350 "Hello, World!".to_string(),
351 );
352
353 let message_id = engine.store(&message).await.unwrap();
354 let content = engine
355 .retrieve("recipient@example.com", &message_id)
356 .await
357 .unwrap();
358
359 assert!(content.contains("Hello, World!"));
360 assert!(content.contains("From: sender@example.com"));
361 }
362
363 #[tokio::test]
364 async fn test_file_storage_list() {
365 let temp_dir = TempDir::new().unwrap();
366 let engine = FileStorageEngine::without_metadata(temp_dir.path().to_path_buf());
367
368 let msg1 = EmailMessage::new(
369 "sender@example.com".to_string(),
370 "user@example.com".to_string(),
371 "Message 1".to_string(),
372 );
373 let msg2 = EmailMessage::new(
374 "sender@example.com".to_string(),
375 "user@example.com".to_string(),
376 "Message 2".to_string(),
377 );
378
379 let id1 = engine.store(&msg1).await.unwrap();
380 let id2 = engine.store(&msg2).await.unwrap();
381
382 let messages = engine.list("user@example.com").await.unwrap();
383 assert_eq!(messages.len(), 2);
384 assert!(messages.contains(&id1));
385 assert!(messages.contains(&id2));
386 }
387
388 #[tokio::test]
389 async fn test_file_storage_delete() {
390 let temp_dir = TempDir::new().unwrap();
391 let engine = FileStorageEngine::without_metadata(temp_dir.path().to_path_buf());
392
393 let message = EmailMessage::new(
394 "sender@example.com".to_string(),
395 "recipient@example.com".to_string(),
396 "Test message".to_string(),
397 );
398
399 let message_id = engine.store(&message).await.unwrap();
400 assert!(engine
401 .retrieve("recipient@example.com", &message_id)
402 .await
403 .is_ok());
404
405 engine
406 .delete("recipient@example.com", &message_id)
407 .await
408 .unwrap();
409 assert!(matches!(
410 engine.retrieve("recipient@example.com", &message_id).await,
411 Err(StorageError::NotFound)
412 ));
413 }
414
415 #[tokio::test]
416 async fn test_file_storage_list_empty() {
417 let temp_dir = TempDir::new().unwrap();
418 let engine = FileStorageEngine::without_metadata(temp_dir.path().to_path_buf());
419
420 let messages = engine.list("nonexistent@example.com").await.unwrap();
421 assert!(messages.is_empty());
422 }
423
424 #[tokio::test]
425 async fn test_file_storage_create_mailbox() {
426 let temp_dir = TempDir::new().unwrap();
427 let engine = FileStorageEngine::without_metadata(temp_dir.path().to_path_buf());
428
429 engine
430 .create_mailbox("user@example.com", "Drafts")
431 .await
432 .unwrap();
433
434 let mailbox_path = temp_dir.path().join("user_example_com").join("Drafts.mbox");
435 assert!(mailbox_path.exists());
436 }
437
438 #[tokio::test]
439 async fn test_storage_error_display() {
440 assert_eq!(StorageError::NotFound.to_string(), "Message not found");
441 assert_eq!(
442 StorageError::EngineError("test".to_string()).to_string(),
443 "Storage error: test"
444 );
445 }
446
447 #[tokio::test]
450 async fn test_memory_storage_new() {
451 let engine = MemoryStorageEngine::new();
452 assert_eq!(engine.total_message_count(), 0);
453 }
454
455 #[tokio::test]
456 async fn test_memory_storage_store_and_retrieve() {
457 let engine = MemoryStorageEngine::new();
458
459 let message = EmailMessage::new(
460 "sender@example.com".to_string(),
461 "recipient@example.com".to_string(),
462 "Hello, World!".to_string(),
463 );
464
465 let message_id = engine.store(&message).await.unwrap();
466 let content = engine
467 .retrieve("recipient@example.com", &message_id)
468 .await
469 .unwrap();
470
471 assert_eq!(content, "Hello, World!");
472 assert_eq!(engine.message_count("recipient@example.com"), 1);
473 }
474
475 #[tokio::test]
476 async fn test_memory_storage_store_multiple_users() {
477 let engine = MemoryStorageEngine::new();
478
479 let msg1 = EmailMessage::new(
480 "sender@example.com".to_string(),
481 "user1@example.com".to_string(),
482 "Message for user1".to_string(),
483 );
484 let msg2 = EmailMessage::new(
485 "sender@example.com".to_string(),
486 "user2@example.com".to_string(),
487 "Message for user2".to_string(),
488 );
489
490 engine.store(&msg1).await.unwrap();
491 engine.store(&msg2).await.unwrap();
492
493 assert_eq!(engine.message_count("user1@example.com"), 1);
494 assert_eq!(engine.message_count("user2@example.com"), 1);
495 assert_eq!(engine.total_message_count(), 2);
496 }
497
498 #[tokio::test]
499 async fn test_memory_storage_list() {
500 let engine = MemoryStorageEngine::new();
501
502 let msg1 = EmailMessage::new(
503 "sender@example.com".to_string(),
504 "user@example.com".to_string(),
505 "Message 1".to_string(),
506 );
507 let msg2 = EmailMessage::new(
508 "sender@example.com".to_string(),
509 "user@example.com".to_string(),
510 "Message 2".to_string(),
511 );
512
513 let id1 = engine.store(&msg1).await.unwrap();
514 let id2 = engine.store(&msg2).await.unwrap();
515
516 let messages = engine.list("user@example.com").await.unwrap();
517 assert_eq!(messages.len(), 2);
518 assert!(messages.contains(&id1));
519 assert!(messages.contains(&id2));
520 }
521
522 #[tokio::test]
523 async fn test_memory_storage_list_empty() {
524 let engine = MemoryStorageEngine::new();
525
526 let messages = engine.list("nonexistent@example.com").await.unwrap();
527 assert!(messages.is_empty());
528 }
529
530 #[tokio::test]
531 async fn test_memory_storage_delete() {
532 let engine = MemoryStorageEngine::new();
533
534 let message = EmailMessage::new(
535 "sender@example.com".to_string(),
536 "recipient@example.com".to_string(),
537 "Test message".to_string(),
538 );
539
540 let message_id = engine.store(&message).await.unwrap();
541 assert_eq!(engine.message_count("recipient@example.com"), 1);
542
543 engine
544 .delete("recipient@example.com", &message_id)
545 .await
546 .unwrap();
547 assert_eq!(engine.message_count("recipient@example.com"), 0);
548
549 assert!(matches!(
551 engine.retrieve("recipient@example.com", &message_id).await,
552 Err(StorageError::NotFound)
553 ));
554 }
555
556 #[tokio::test]
557 async fn test_memory_storage_delete_nonexistent() {
558 let engine = MemoryStorageEngine::new();
559
560 let result = engine.delete("user@example.com", "fake-id").await;
562 assert!(matches!(result, Err(StorageError::NotFound)));
563
564 let message = EmailMessage::new(
566 "sender@example.com".to_string(),
567 "user@example.com".to_string(),
568 "Test".to_string(),
569 );
570 engine.store(&message).await.unwrap();
571
572 let result = engine.delete("user@example.com", "wrong-id").await;
573 assert!(matches!(result, Err(StorageError::NotFound)));
574 }
575
576 #[tokio::test]
577 async fn test_memory_storage_retrieve_nonexistent() {
578 let engine = MemoryStorageEngine::new();
579
580 let result = engine.retrieve("user@example.com", "fake-id").await;
582 assert!(matches!(result, Err(StorageError::NotFound)));
583
584 let message = EmailMessage::new(
586 "sender@example.com".to_string(),
587 "user@example.com".to_string(),
588 "Test".to_string(),
589 );
590 engine.store(&message).await.unwrap();
591
592 let result = engine.retrieve("user@example.com", "wrong-id").await;
593 assert!(matches!(result, Err(StorageError::NotFound)));
594 }
595
596 #[tokio::test]
597 async fn test_memory_storage_create_mailbox() {
598 let engine = MemoryStorageEngine::new();
599
600 engine
601 .create_mailbox("user@example.com", "Inbox")
602 .await
603 .unwrap();
604 engine
605 .create_mailbox("user@example.com", "Drafts")
606 .await
607 .unwrap();
608
609 engine
611 .create_mailbox("user@example.com", "Inbox")
612 .await
613 .unwrap();
614 }
615
616 #[tokio::test]
617 async fn test_memory_storage_clear() {
618 let engine = MemoryStorageEngine::new();
619
620 let msg1 = EmailMessage::new(
621 "sender@example.com".to_string(),
622 "user1@example.com".to_string(),
623 "Message 1".to_string(),
624 );
625 let msg2 = EmailMessage::new(
626 "sender@example.com".to_string(),
627 "user2@example.com".to_string(),
628 "Message 2".to_string(),
629 );
630
631 engine.store(&msg1).await.unwrap();
632 engine.store(&msg2).await.unwrap();
633 assert_eq!(engine.total_message_count(), 2);
634
635 engine.clear();
636 assert_eq!(engine.total_message_count(), 0);
637 }
638
639 #[tokio::test]
640 async fn test_memory_storage_with_id() {
641 let engine = MemoryStorageEngine::new();
642
643 let message = EmailMessage::with_id(
644 "custom-id-123".to_string(),
645 "sender@example.com".to_string(),
646 "recipient@example.com".to_string(),
647 "Test Subject".to_string(),
648 "Test body content".to_string(),
649 );
650
651 let message_id = engine.store(&message).await.unwrap();
652 assert_eq!(message_id, "custom-id-123");
653
654 let content = engine
655 .retrieve("recipient@example.com", "custom-id-123")
656 .await
657 .unwrap();
658 assert!(content.contains("Test body content"));
659 assert!(content.contains("Subject: Test Subject"));
660 }
661
662 #[tokio::test]
663 async fn test_memory_storage_special_characters_in_user() {
664 let engine = MemoryStorageEngine::new();
665
666 let message = EmailMessage::new(
667 "sender@example.com".to_string(),
668 "user+tag@example.com".to_string(),
669 "Test message".to_string(),
670 );
671
672 let message_id = engine.store(&message).await.unwrap();
673
674 let content = engine
676 .retrieve("user+tag@example.com", &message_id)
677 .await
678 .unwrap();
679 assert_eq!(content, "Test message");
680
681 assert_eq!(engine.message_count("user+tag@example.com"), 1);
683 }
684}