use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use crate::email::Email;
use crate::error::MailError;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeliveryResult {
pub message_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub provider_response: Option<serde_json::Value>,
}
impl DeliveryResult {
pub fn new(message_id: impl Into<String>) -> Self {
Self {
message_id: message_id.into(),
provider_response: None,
}
}
pub fn with_response(message_id: impl Into<String>, response: serde_json::Value) -> Self {
Self {
message_id: message_id.into(),
provider_response: Some(response),
}
}
}
#[async_trait]
pub trait Mailer: Send + Sync {
async fn deliver(&self, email: &Email) -> Result<DeliveryResult, MailError>;
fn validate_batch(&self, _emails: &[Email]) -> Result<(), MailError> {
Ok(()) }
async fn deliver_many(&self, emails: &[Email]) -> Result<Vec<DeliveryResult>, MailError> {
self.validate_batch(emails)?;
let mut results = Vec::with_capacity(emails.len());
for email in emails {
results.push(self.deliver(email).await?);
}
Ok(results)
}
fn provider_name(&self) -> &'static str {
"unknown"
}
fn validate_config(&self) -> Result<(), MailError> {
Ok(())
}
}
pub trait MailerExt: Mailer {
#[deprecated(
since = "0.6.2",
note = "does not handle EMAIL_FROM fallback; use Email::is_valid() or rely on deliver() validation"
)]
fn validate(&self, email: &Email) -> Result<(), MailError> {
if email.from.is_none() {
return Err(MailError::MissingField("from"));
}
if email.to.is_empty() {
return Err(MailError::MissingField("to"));
}
Ok(())
}
}
impl<T: Mailer> MailerExt for T {}