rok-mail 0.6.0

Email support for the rok ecosystem — Mailable trait, log/SMTP drivers
Documentation
use std::sync::Arc;

use async_trait::async_trait;
use rok_queue::{Job, JobContext, JobResult, Queue, Runnable};
use serde::{Deserialize, Serialize};

use crate::Mailable;

/// A reference-counted queue handle used via task-local storage.
pub(crate) type QueueRef = Arc<Queue>;

/// Background job that sends an email that was queued via `Mail::queue()` or
/// `Mail::later()`.
///
/// The mailable content (subject, body, HTML body) is rendered at dispatch
/// time and serialised into this struct. The actual send happens when the
/// worker picks up this job, using either the global mail config (set via
/// [`crate::mail::set_global_config`]) or a fallback "log" config.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SendMailJob {
    pub to: String,
    pub subject: String,
    pub body: String,
    pub html_body: Option<String>,
}

impl Job for SendMailJob {
    fn job_type() -> &'static str
    where
        Self: Sized,
    {
        "rok_mail::SendMailJob"
    }

    fn queue() -> &'static str
    where
        Self: Sized,
    {
        "mail"
    }

    fn max_attempts() -> u32
    where
        Self: Sized,
    {
        3
    }
}

#[async_trait]
impl Runnable for SendMailJob {
    async fn run(&self, _ctx: &JobContext) -> JobResult {
        use crate::mail::global_config;

        let config = global_config().cloned().unwrap_or_default();

        // Reconstruct a minimal mailable so we can re-use the send path.
        struct QueuedMailable {
            subject: String,
            body: String,
            html_body: Option<String>,
        }

        impl Mailable for QueuedMailable {
            fn subject(&self) -> &str {
                &self.subject
            }
            fn body(&self) -> String {
                self.body.clone()
            }
            fn html_body(&self) -> Option<String> {
                self.html_body.clone()
            }
        }

        let mailable = QueuedMailable {
            subject: self.subject.clone(),
            body: self.body.clone(),
            html_body: self.html_body.clone(),
        };

        crate::Mail::send_with(mailable, &self.to, &config)
            .await
            .map_err(|e| rok_queue::JobError(e.to_string()))
    }
}

/// Register all mail-related job types with a `JobRegistry`.
pub fn register_mail_jobs(registry: &mut rok_queue::JobRegistry) {
    registry.register::<SendMailJob>();
}