use async_trait::async_trait;
use crate::{
EmailResult, EmailMessage, EmailDeliveryReport,
EmailTemplate, EmailStatus
};
use std::collections::HashMap;
#[async_trait]
pub trait EmailService: Send + Sync {
async fn send(&self, message: EmailMessage) -> EmailResult<EmailDeliveryReport>;
async fn send_bulk(&self, messages: Vec<EmailMessage>) -> EmailResult<Vec<EmailDeliveryReport>> {
let mut reports = Vec::with_capacity(messages.len());
for message in messages {
let report = self.send(message).await?;
reports.push(report);
}
Ok(reports)
}
async fn send_template(
&self,
template_name: &str,
recipients: Vec<String>,
template_data: HashMap<String, serde_json::Value>,
) -> EmailResult<Vec<EmailDeliveryReport>>;
async fn get_delivery_status(&self, message_id: &str) -> EmailResult<Option<EmailDeliveryReport>>;
async fn cancel_scheduled(&self, message_id: &str) -> EmailResult<bool>;
async fn get_stats(&self) -> EmailResult<EmailServiceStats>;
async fn validate_config(&self) -> EmailResult<bool>;
async fn test_connection(&self) -> EmailResult<bool>;
}
#[async_trait]
pub trait TemplateManager: Send + Sync {
async fn create_template(&self, template: EmailTemplate) -> EmailResult<String>;
async fn get_template(&self, name: &str) -> EmailResult<Option<EmailTemplate>>;
async fn update_template(&self, name: &str, template: EmailTemplate) -> EmailResult<bool>;
async fn delete_template(&self, name: &str) -> EmailResult<bool>;
async fn list_templates(&self) -> EmailResult<Vec<String>>;
async fn render_template(
&self,
template_name: &str,
data: &HashMap<String, serde_json::Value>,
) -> EmailResult<TemplateRenderResult>;
}
#[derive(Debug, Clone)]
pub struct TemplateRenderResult {
pub subject: String,
pub html: String,
pub text: Option<String>,
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, Default)]
pub struct EmailServiceStats {
pub total_sent: u64,
pub total_delivered: u64,
pub total_failed: u64,
pub total_bounced: u64,
pub total_opened: u64,
pub total_clicked: u64,
pub avg_delivery_time_ms: Option<f64>,
pub queue_size: u64,
pub rate_limit_remaining: Option<u64>,
pub last_activity: Option<chrono::DateTime<chrono::Utc>>,
pub provider_stats: HashMap<String, serde_json::Value>,
}
impl EmailServiceStats {
pub fn delivery_rate(&self) -> f64 {
if self.total_sent > 0 {
self.total_delivered as f64 / self.total_sent as f64
} else {
0.0
}
}
pub fn failure_rate(&self) -> f64 {
if self.total_sent > 0 {
self.total_failed as f64 / self.total_sent as f64
} else {
0.0
}
}
pub fn bounce_rate(&self) -> f64 {
if self.total_delivered > 0 {
self.total_bounced as f64 / self.total_delivered as f64
} else {
0.0
}
}
pub fn open_rate(&self) -> f64 {
if self.total_delivered > 0 {
self.total_opened as f64 / self.total_delivered as f64
} else {
0.0
}
}
pub fn click_rate(&self) -> f64 {
if self.total_opened > 0 {
self.total_clicked as f64 / self.total_opened as f64
} else {
0.0
}
}
pub fn update_with_delivery_report(&mut self, report: &EmailDeliveryReport) {
match report.status {
EmailStatus::Sent => self.total_sent += 1,
EmailStatus::Failed => {
self.total_sent += 1;
self.total_failed += 1;
}
EmailStatus::Bounced => {
self.total_delivered += 1;
self.total_bounced += 1;
}
EmailStatus::Opened => self.total_opened += 1,
EmailStatus::Clicked => self.total_clicked += 1,
EmailStatus::Pending => {} }
use chrono::Utc;
self.last_activity = Some(Utc::now());
}
}
pub trait EmailProviderConfig {
fn provider_name(&self) -> &'static str;
fn validate(&self) -> Result<(), String>;
fn to_json(&self) -> Result<serde_json::Value, serde_json::Error>;
}
#[async_trait]
pub trait WebhookHandler: Send + Sync {
async fn handle_delivery_webhook(
&self,
payload: serde_json::Value,
signature: Option<String>,
) -> EmailResult<EmailDeliveryReport>;
async fn handle_bounce_webhook(
&self,
payload: serde_json::Value,
signature: Option<String>,
) -> EmailResult<EmailDeliveryReport>;
async fn handle_open_webhook(
&self,
payload: serde_json::Value,
signature: Option<String>,
) -> EmailResult<EmailDeliveryReport>;
async fn handle_click_webhook(
&self,
payload: serde_json::Value,
signature: Option<String>,
) -> EmailResult<EmailDeliveryReport>;
fn verify_signature(&self, payload: &str, signature: &str) -> bool;
}
#[async_trait]
pub trait EmailQueue: Send + Sync {
async fn enqueue(&self, message: EmailMessage, scheduled_at: Option<chrono::DateTime<chrono::Utc>>) -> EmailResult<String>;
async fn dequeue(&self) -> EmailResult<Option<EmailMessage>>;
async fn size(&self) -> EmailResult<usize>;
async fn clear(&self) -> EmailResult<()>;
async fn remove(&self, message_id: &str) -> EmailResult<bool>;
async fn get_scheduled(&self, limit: usize) -> EmailResult<Vec<EmailMessage>>;
}