use chrono::{DateTime, Utc};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use crate::email::Email;
#[derive(Debug, Clone)]
pub struct StoredEmail {
pub id: String,
pub email: Email,
pub sent_at: DateTime<Utc>,
}
pub trait Storage: Send + Sync {
fn push(&self, email: Email) -> String;
fn pop(&self) -> Option<StoredEmail>;
fn get(&self, id: &str) -> Option<StoredEmail>;
fn all(&self) -> Vec<StoredEmail>;
fn delete(&self, id: &str) -> bool;
fn clear(&self);
fn count(&self) -> usize;
fn flush(&self) -> Vec<StoredEmail>;
}
#[derive(Debug, Default)]
pub struct MemoryStorage {
emails: RwLock<HashMap<String, StoredEmail>>,
order: RwLock<Vec<String>>,
}
impl MemoryStorage {
pub fn new() -> Self {
Self::default()
}
pub fn shared() -> Arc<Self> {
Arc::new(Self::new())
}
}
impl Storage for MemoryStorage {
fn push(&self, email: Email) -> String {
let id = uuid::Uuid::new_v4().to_string();
let sent_at = Utc::now();
let mut email = email;
email.private.insert(
"sent_at".to_string(),
serde_json::json!(sent_at.to_rfc3339()),
);
let stored = StoredEmail {
id: id.clone(),
email,
sent_at,
};
{
let mut emails = self.emails.write().unwrap();
let mut order = self.order.write().unwrap();
emails.insert(id.clone(), stored);
order.push(id.clone());
}
id
}
fn pop(&self) -> Option<StoredEmail> {
let mut emails = self.emails.write().unwrap();
let mut order = self.order.write().unwrap();
if let Some(id) = order.pop() {
emails.remove(&id)
} else {
None
}
}
fn get(&self, id: &str) -> Option<StoredEmail> {
let emails = self.emails.read().unwrap();
emails.get(id).cloned()
}
fn all(&self) -> Vec<StoredEmail> {
let emails = self.emails.read().unwrap();
let order = self.order.read().unwrap();
order
.iter()
.rev()
.filter_map(|id| emails.get(id).cloned())
.collect()
}
fn delete(&self, id: &str) -> bool {
let mut emails = self.emails.write().unwrap();
let mut order = self.order.write().unwrap();
if emails.remove(id).is_some() {
order.retain(|x| x != id);
true
} else {
false
}
}
fn clear(&self) {
let mut emails = self.emails.write().unwrap();
let mut order = self.order.write().unwrap();
emails.clear();
order.clear();
}
fn count(&self) -> usize {
let emails = self.emails.read().unwrap();
emails.len()
}
fn flush(&self) -> Vec<StoredEmail> {
let mut emails = self.emails.write().unwrap();
let mut order = self.order.write().unwrap();
let result: Vec<StoredEmail> = order
.iter()
.rev()
.filter_map(|id| emails.get(id).cloned())
.collect();
emails.clear();
order.clear();
result
}
}
impl Storage for Arc<MemoryStorage> {
fn push(&self, email: Email) -> String {
(**self).push(email)
}
fn pop(&self) -> Option<StoredEmail> {
(**self).pop()
}
fn get(&self, id: &str) -> Option<StoredEmail> {
(**self).get(id)
}
fn all(&self) -> Vec<StoredEmail> {
(**self).all()
}
fn delete(&self, id: &str) -> bool {
(**self).delete(id)
}
fn clear(&self) {
(**self).clear()
}
fn count(&self) -> usize {
(**self).count()
}
fn flush(&self) -> Vec<StoredEmail> {
(**self).flush()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_storage() {
let storage = MemoryStorage::new();
let email = Email::new()
.from("test@example.com")
.to("recipient@example.com")
.subject("Test");
let id = storage.push(email.clone());
assert_eq!(storage.count(), 1);
let stored = storage.get(&id).unwrap();
assert_eq!(stored.email.subject, "Test");
let email2 = Email::new().subject("Second");
let id2 = storage.push(email2);
let all = storage.all();
assert_eq!(all.len(), 2);
assert_eq!(all[0].id, id2);
assert!(storage.delete(&id));
assert_eq!(storage.count(), 1);
assert!(storage.get(&id).is_none());
storage.clear();
assert_eq!(storage.count(), 0);
}
#[test]
fn test_flush() {
let storage = MemoryStorage::new();
let email1 = Email::new()
.from("test@example.com")
.to("recipient@example.com")
.subject("First");
let email2 = Email::new()
.from("test@example.com")
.to("recipient@example.com")
.subject("Second");
let email3 = Email::new()
.from("test@example.com")
.to("recipient@example.com")
.subject("Third");
storage.push(email1);
storage.push(email2);
storage.push(email3);
assert_eq!(storage.count(), 3);
let flushed = storage.flush();
assert_eq!(flushed.len(), 3);
assert_eq!(flushed[0].email.subject, "Third"); assert_eq!(flushed[1].email.subject, "Second");
assert_eq!(flushed[2].email.subject, "First");
assert_eq!(storage.count(), 0);
assert!(storage.all().is_empty());
let empty_flush = storage.flush();
assert!(empty_flush.is_empty());
}
}