use crate::error::Result;
use crate::traits::mailer::{Email, Mailer};
use async_trait::async_trait;
#[derive(Debug, Clone)]
pub struct ConsoleMailer {
prefix: String,
show_full_content: bool,
}
impl ConsoleMailer {
pub fn new() -> Self {
Self {
prefix: "[EMAIL]".to_string(),
show_full_content: false,
}
}
pub fn with_prefix(prefix: impl Into<String>) -> Self {
Self {
prefix: prefix.into(),
show_full_content: false,
}
}
pub fn with_full_output(mut self, enabled: bool) -> Self {
if enabled {
tracing::warn!(
"ConsoleMailer: full output enabled - email content will be visible in logs. \
Do not use in production!"
);
}
self.show_full_content = enabled;
self
}
}
impl Default for ConsoleMailer {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl Mailer for ConsoleMailer {
async fn send(&self, email: &Email) -> Result<()> {
email.validate()?;
println!("{} ════════════════════════════════════════", self.prefix);
println!("{} From: {}", self.prefix, email.from);
println!("{} To: {} recipient(s)", self.prefix, email.to.len());
if !email.cc.is_empty() {
println!("{} CC: {} recipient(s)", self.prefix, email.cc.len());
}
if !email.bcc.is_empty() {
println!("{} BCC: {} recipient(s)", self.prefix, email.bcc.len());
}
if email.reply_to.is_some() {
println!("{} Reply-To: [set]", self.prefix);
}
println!("{} Subject: {}", self.prefix, email.subject);
println!("{} ────────────────────────────────────────", self.prefix);
if self.show_full_content {
if let Some(ref text) = email.text {
println!("{} [TEXT]", self.prefix);
for line in text.lines() {
println!("{} {}", self.prefix, line);
}
}
if let Some(ref html) = email.html {
println!("{} [HTML]", self.prefix);
for line in html.lines() {
println!("{} {}", self.prefix, line);
}
}
} else {
if let Some(ref text) = email.text {
println!("{} [TEXT] {} bytes [REDACTED]", self.prefix, text.len());
}
if let Some(ref html) = email.html {
println!("{} [HTML] {} bytes [REDACTED]", self.prefix, html.len());
}
}
println!("{} ════════════════════════════════════════", self.prefix);
Ok(())
}
fn is_healthy(&self) -> bool {
true }
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_console_mailer_sends_without_error() {
let mailer = ConsoleMailer::new();
let email = Email::new("from@test.com", "to@test.com", "Test Subject").text("Test body");
let result = mailer.send(&email).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_console_mailer_validates_email() {
let mailer = ConsoleMailer::new();
let email = Email::new("from@test.com", "to@test.com", "Test Subject");
let result = mailer.send(&email).await;
assert!(result.is_err());
}
#[test]
fn test_console_mailer_is_healthy() {
let mailer = ConsoleMailer::new();
assert!(mailer.is_healthy());
}
}