Skip to main content

backbone_email/
traits.rs

1//! Email service traits
2
3use async_trait::async_trait;
4use crate::{
5    EmailResult, EmailMessage, EmailDeliveryReport,
6    EmailTemplate, EmailStatus
7};
8use std::collections::HashMap;
9
10/// Generic email service trait
11#[async_trait]
12pub trait EmailService: Send + Sync {
13    /// Send single email message
14    async fn send(&self, message: EmailMessage) -> EmailResult<EmailDeliveryReport>;
15
16    /// Send multiple email messages (bulk send)
17    async fn send_bulk(&self, messages: Vec<EmailMessage>) -> EmailResult<Vec<EmailDeliveryReport>> {
18        let mut reports = Vec::with_capacity(messages.len());
19
20        for message in messages {
21            let report = self.send(message).await?;
22            reports.push(report);
23        }
24
25        Ok(reports)
26    }
27
28    /// Send email using template
29    async fn send_template(
30        &self,
31        template_name: &str,
32        recipients: Vec<String>,
33        template_data: HashMap<String, serde_json::Value>,
34    ) -> EmailResult<Vec<EmailDeliveryReport>>;
35
36    /// Get delivery status for a message
37    async fn get_delivery_status(&self, message_id: &str) -> EmailResult<Option<EmailDeliveryReport>>;
38
39    /// Cancel scheduled email
40    async fn cancel_scheduled(&self, message_id: &str) -> EmailResult<bool>;
41
42    /// Get email service statistics
43    async fn get_stats(&self) -> EmailResult<EmailServiceStats>;
44
45    /// Validate email configuration
46    async fn validate_config(&self) -> EmailResult<bool>;
47
48    /// Test email delivery
49    async fn test_connection(&self) -> EmailResult<bool>;
50}
51
52/// Template management trait
53#[async_trait]
54pub trait TemplateManager: Send + Sync {
55    /// Create new email template
56    async fn create_template(&self, template: EmailTemplate) -> EmailResult<String>;
57
58    /// Get email template by name
59    async fn get_template(&self, name: &str) -> EmailResult<Option<EmailTemplate>>;
60
61    /// Update email template
62    async fn update_template(&self, name: &str, template: EmailTemplate) -> EmailResult<bool>;
63
64    /// Delete email template
65    async fn delete_template(&self, name: &str) -> EmailResult<bool>;
66
67    /// List all templates
68    async fn list_templates(&self) -> EmailResult<Vec<String>>;
69
70    /// Render template with data
71    async fn render_template(
72        &self,
73        template_name: &str,
74        data: &HashMap<String, serde_json::Value>,
75    ) -> EmailResult<TemplateRenderResult>;
76}
77
78/// Template rendering result
79#[derive(Debug, Clone)]
80pub struct TemplateRenderResult {
81    /// Rendered subject
82    pub subject: String,
83
84    /// Rendered HTML content
85    pub html: String,
86
87    /// Rendered plain text content
88    pub text: Option<String>,
89
90    /// Render metadata
91    pub metadata: HashMap<String, String>,
92}
93
94/// Email service statistics
95#[derive(Debug, Clone, Default)]
96pub struct EmailServiceStats {
97    /// Total emails sent
98    pub total_sent: u64,
99
100    /// Total emails delivered
101    pub total_delivered: u64,
102
103    /// Total emails failed
104    pub total_failed: u64,
105
106    /// Total emails bounced
107    pub total_bounced: u64,
108
109    /// Total emails opened (if tracking enabled)
110    pub total_opened: u64,
111
112    /// Total emails clicked (if tracking enabled)
113    pub total_clicked: u64,
114
115    /// Average delivery time in milliseconds
116    pub avg_delivery_time_ms: Option<f64>,
117
118    /// Current queue size (scheduled emails)
119    pub queue_size: u64,
120
121    /// Rate limit remaining (if applicable)
122    pub rate_limit_remaining: Option<u64>,
123
124    /// Last activity timestamp
125    pub last_activity: Option<chrono::DateTime<chrono::Utc>>,
126
127    /// Provider-specific statistics
128    pub provider_stats: HashMap<String, serde_json::Value>,
129}
130
131impl EmailServiceStats {
132    /// Calculate delivery rate
133    pub fn delivery_rate(&self) -> f64 {
134        if self.total_sent > 0 {
135            self.total_delivered as f64 / self.total_sent as f64
136        } else {
137            0.0
138        }
139    }
140
141    /// Calculate failure rate
142    pub fn failure_rate(&self) -> f64 {
143        if self.total_sent > 0 {
144            self.total_failed as f64 / self.total_sent as f64
145        } else {
146            0.0
147        }
148    }
149
150    /// Calculate bounce rate
151    pub fn bounce_rate(&self) -> f64 {
152        if self.total_delivered > 0 {
153            self.total_bounced as f64 / self.total_delivered as f64
154        } else {
155            0.0
156        }
157    }
158
159    /// Calculate open rate (if tracking enabled)
160    pub fn open_rate(&self) -> f64 {
161        if self.total_delivered > 0 {
162            self.total_opened as f64 / self.total_delivered as f64
163        } else {
164            0.0
165        }
166    }
167
168    /// Calculate click rate (if tracking enabled)
169    pub fn click_rate(&self) -> f64 {
170        if self.total_opened > 0 {
171            self.total_clicked as f64 / self.total_opened as f64
172        } else {
173            0.0
174        }
175    }
176
177    /// Update statistics with new delivery report
178    pub fn update_with_delivery_report(&mut self, report: &EmailDeliveryReport) {
179        match report.status {
180            EmailStatus::Sent => self.total_sent += 1,
181            EmailStatus::Failed => {
182                self.total_sent += 1;
183                self.total_failed += 1;
184            }
185            EmailStatus::Bounced => {
186                self.total_delivered += 1;
187                self.total_bounced += 1;
188            }
189            EmailStatus::Opened => self.total_opened += 1,
190            EmailStatus::Clicked => self.total_clicked += 1,
191            EmailStatus::Pending => {} // No update for pending
192        }
193
194        // Update last activity
195        use chrono::Utc;
196        self.last_activity = Some(Utc::now());
197    }
198}
199
200/// Email provider configuration trait
201pub trait EmailProviderConfig {
202    /// Get provider name
203    fn provider_name(&self) -> &'static str;
204
205    /// Validate configuration
206    fn validate(&self) -> Result<(), String>;
207
208    /// Get configuration as JSON
209    fn to_json(&self) -> Result<serde_json::Value, serde_json::Error>;
210}
211
212/// Email delivery webhook handler trait
213#[async_trait]
214pub trait WebhookHandler: Send + Sync {
215    /// Handle delivery webhook
216    async fn handle_delivery_webhook(
217        &self,
218        payload: serde_json::Value,
219        signature: Option<String>,
220    ) -> EmailResult<EmailDeliveryReport>;
221
222    /// Handle bounce webhook
223    async fn handle_bounce_webhook(
224        &self,
225        payload: serde_json::Value,
226        signature: Option<String>,
227    ) -> EmailResult<EmailDeliveryReport>;
228
229    /// Handle open webhook
230    async fn handle_open_webhook(
231        &self,
232        payload: serde_json::Value,
233        signature: Option<String>,
234    ) -> EmailResult<EmailDeliveryReport>;
235
236    /// Handle click webhook
237    async fn handle_click_webhook(
238        &self,
239        payload: serde_json::Value,
240        signature: Option<String>,
241    ) -> EmailResult<EmailDeliveryReport>;
242
243    /// Verify webhook signature
244    fn verify_signature(&self, payload: &str, signature: &str) -> bool;
245}
246
247/// Email queue management trait
248#[async_trait]
249pub trait EmailQueue: Send + Sync {
250    /// Add email to queue
251    async fn enqueue(&self, message: EmailMessage, scheduled_at: Option<chrono::DateTime<chrono::Utc>>) -> EmailResult<String>;
252
253    /// Get next email from queue
254    async fn dequeue(&self) -> EmailResult<Option<EmailMessage>>;
255
256    /// Get queue size
257    async fn size(&self) -> EmailResult<usize>;
258
259    /// Clear queue
260    async fn clear(&self) -> EmailResult<()>;
261
262    /// Remove specific email from queue
263    async fn remove(&self, message_id: &str) -> EmailResult<bool>;
264
265    /// Get scheduled emails
266    async fn get_scheduled(&self, limit: usize) -> EmailResult<Vec<EmailMessage>>;
267}