rok-mail 0.6.0

Email support for the rok ecosystem — Mailable trait, log/SMTP drivers
Documentation
/// Implement this trait on any struct that represents a sendable email.
///
/// The `Send + Sync` bounds are required so that async handlers holding a
/// `Mailable` across an `.await` point compile without `Handler` bound errors.
pub trait Mailable: Send + Sync {
    fn subject(&self) -> &str;
    fn body(&self) -> String;
    fn html_body(&self) -> Option<String> {
        None
    }
}

/// A mailable that renders Markdown content as the HTML body.
///
/// The plain-text body is the raw Markdown source; the HTML body is the
/// rendered output.
///
/// # Example
///
/// ```rust,ignore
/// use rok_mail::MarkdownMailable;
///
/// let mailable = MarkdownMailable::new(
///     "Welcome!",
///     "# Hello\n\nThis is a **bold** welcome message.",
/// );
/// ```
#[cfg(feature = "markdown")]
pub struct MarkdownMailable {
    subject: String,
    markdown: String,
}

#[cfg(feature = "markdown")]
impl MarkdownMailable {
    pub fn new(subject: impl Into<String>, markdown: impl Into<String>) -> Self {
        Self {
            subject: subject.into(),
            markdown: markdown.into(),
        }
    }

    /// Replace the subject (builder-style).
    pub fn subject(mut self, subject: impl Into<String>) -> Self {
        self.subject = subject.into();
        self
    }

    /// Replace the markdown body (builder-style).
    pub fn body(mut self, markdown: impl Into<String>) -> Self {
        self.markdown = markdown.into();
        self
    }
}

#[cfg(feature = "markdown")]
impl Mailable for MarkdownMailable {
    fn subject(&self) -> &str {
        &self.subject
    }

    fn body(&self) -> String {
        self.markdown.clone()
    }

    fn html_body(&self) -> Option<String> {
        let parser = pulldown_cmark::Parser::new(&self.markdown);
        let mut html = String::new();
        pulldown_cmark::html::push_html(&mut html, parser);
        Some(html)
    }
}