use async_trait::async_trait;
use crate::email::Email;
use crate::error::MailError;
use crate::mailer::{DeliveryResult, Mailer};
pub trait Interceptor: Send + Sync {
fn intercept(&self, email: Email) -> Result<Email, MailError>;
}
impl<F> Interceptor for F
where
F: Fn(Email) -> Result<Email, MailError> + Send + Sync,
{
fn intercept(&self, email: Email) -> Result<Email, MailError> {
(self)(email)
}
}
#[derive(Debug)]
pub struct WithInterceptor<M, I> {
inner: M,
interceptor: I,
}
impl<M: Clone, I: Clone> Clone for WithInterceptor<M, I> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
interceptor: self.interceptor.clone(),
}
}
}
impl<M, I> WithInterceptor<M, I> {
pub(crate) fn new(inner: M, interceptor: I) -> Self {
Self { inner, interceptor }
}
}
#[async_trait]
impl<M, I> Mailer for WithInterceptor<M, I>
where
M: Mailer,
I: Interceptor,
{
async fn deliver(&self, email: &Email) -> Result<DeliveryResult, MailError> {
let email = self.interceptor.intercept(email.clone())?;
self.inner.deliver(&email).await
}
async fn deliver_many(&self, emails: &[Email]) -> Result<Vec<DeliveryResult>, MailError> {
let intercepted: Result<Vec<Email>, MailError> = emails
.iter()
.map(|e| self.interceptor.intercept(e.clone()))
.collect();
self.inner.deliver_many(&intercepted?).await
}
fn validate_batch(&self, emails: &[Email]) -> Result<(), MailError> {
self.inner.validate_batch(emails)
}
fn provider_name(&self) -> &'static str {
self.inner.provider_name()
}
fn validate_config(&self) -> Result<(), MailError> {
self.inner.validate_config()
}
}
pub trait InterceptorExt: Mailer + Sized {
fn with_interceptor<I>(self, interceptor: I) -> WithInterceptor<Self, I>
where
I: Interceptor,
{
WithInterceptor::new(self, interceptor)
}
}
impl<M: Mailer + Sized> InterceptorExt for M {}
#[cfg(test)]
mod tests {
use super::*;
struct AddHeader {
name: &'static str,
value: &'static str,
}
impl Interceptor for AddHeader {
fn intercept(&self, email: Email) -> Result<Email, MailError> {
Ok(email.header(self.name, self.value))
}
}
#[test]
fn test_closure_interceptor_compiles() {
fn assert_interceptor<I: Interceptor>(_: I) {}
let closure = |email: Email| -> Result<Email, MailError> { Ok(email) };
assert_interceptor(closure);
}
#[test]
fn test_struct_interceptor_compiles() {
fn assert_interceptor<I: Interceptor>(_: I) {}
let interceptor = AddHeader {
name: "X-Test",
value: "test",
};
assert_interceptor(interceptor);
}
}