gen_mailer 0.2.0

A generic mailer for Rust
Documentation
use std::io;

use async_trait::async_trait;

use crate::Address;
use crate::GenericMailer;
use crate::GenericMailerError;
use crate::Message;

pub struct ConsoleMailer;

#[async_trait]
impl GenericMailer for ConsoleMailer {
    async fn send(&self, m: &Message) -> Result<Vec<String>, GenericMailerError> {
        Self::send(io::stdout(), m)?;

        return Ok(Vec::new());
    }
}

impl ConsoleMailer {
    fn send(mut w: impl io::Write, m: &Message) -> Result<(), std::io::Error> {
        writeln!(
            w,
            "==================== [ BEGIN EMAIL ] ===================="
        )?;
        writeln!(w, "From: {}", m.from)?;

        if let Some(reply_to) = &m.reply_to {
            writeln!(w, "Reply-To: {reply_to}")?;
        }

        if !m.to.is_empty() {
            writeln!(w, "To: {}", join_addresses(&m.to))?;
        }

        if !m.cc.is_empty() {
            writeln!(w, "Cc: {}", join_addresses(&m.cc))?;
        }

        if !m.bcc.is_empty() {
            writeln!(w, "Bcc: {}", join_addresses(&m.bcc))?;
        }

        writeln!(w, "Subject: {}", m.subject)?;
        writeln!(w)?;

        if let Some(body) = &m.text_body {
            writeln!(w, "{body}")?;
        } else {
            writeln!(w, "No text body.")?;
        }

        writeln!(
            w,
            "==================== [  END EMAIL  ] ===================="
        )?;

        return Ok(());
    }
}

fn join_addresses(addrs: &[Address]) -> String {
    return addrs
        .iter()
        .map(|a| a.to_string())
        .collect::<Vec<_>>()
        .join(", ");
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_console_mailer() {
        let message = Message::builder()
            .from(("Sender", "sender@example.com"))
            .to(("Recipient", "recipient@example.com"))
            .cc("cc@example.com")
            .subject("Test Email")
            .text_body("This is a test email.")
            .build()
            .unwrap();

        let expected = [
            "==================== [ BEGIN EMAIL ] ====================",
            "From: \"Sender\" <sender@example.com>",
            "To: \"Recipient\" <recipient@example.com>",
            "Cc: cc@example.com",
            "Subject: Test Email",
            "",
            "This is a test email.",
            "==================== [  END EMAIL  ] ====================",
            "",
        ]
        .join("\n");

        let mut actual = Vec::new();
        ConsoleMailer::send(&mut actual, &message).unwrap();
        let actual = String::from_utf8(actual).unwrap();

        assert_eq!(actual, expected);
    }
}