Skip to main content

ferro_notifications/channels/
mail.rs

1//! Mail notification channel.
2
3use serde::{Deserialize, Serialize};
4
5/// A mail message for email notifications.
6#[derive(Debug, Clone, Default, Serialize, Deserialize)]
7pub struct MailMessage {
8    /// Email subject line.
9    pub subject: String,
10    /// Plain text body.
11    pub body: String,
12    /// Optional HTML body.
13    pub html: Option<String>,
14    /// From address (if different from default).
15    pub from: Option<String>,
16    /// Reply-to address.
17    pub reply_to: Option<String>,
18    /// CC recipients.
19    pub cc: Vec<String>,
20    /// BCC recipients.
21    pub bcc: Vec<String>,
22    /// Custom headers.
23    pub headers: Vec<(String, String)>,
24}
25
26impl MailMessage {
27    /// Create a new empty mail message.
28    pub fn new() -> Self {
29        Self::default()
30    }
31
32    /// Set the subject line.
33    pub fn subject(mut self, subject: impl Into<String>) -> Self {
34        self.subject = subject.into();
35        self
36    }
37
38    /// Set the plain text body.
39    pub fn body(mut self, body: impl Into<String>) -> Self {
40        self.body = body.into();
41        self
42    }
43
44    /// Set the HTML body.
45    pub fn html(mut self, html: impl Into<String>) -> Self {
46        self.html = Some(html.into());
47        self
48    }
49
50    /// Set the from address.
51    pub fn from(mut self, from: impl Into<String>) -> Self {
52        self.from = Some(from.into());
53        self
54    }
55
56    /// Set the reply-to address.
57    pub fn reply_to(mut self, reply_to: impl Into<String>) -> Self {
58        self.reply_to = Some(reply_to.into());
59        self
60    }
61
62    /// Add a CC recipient.
63    pub fn cc(mut self, email: impl Into<String>) -> Self {
64        self.cc.push(email.into());
65        self
66    }
67
68    /// Add a BCC recipient.
69    pub fn bcc(mut self, email: impl Into<String>) -> Self {
70        self.bcc.push(email.into());
71        self
72    }
73
74    /// Add a custom header.
75    pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
76        self.headers.push((name.into(), value.into()));
77        self
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn test_mail_message_builder() {
87        let mail = MailMessage::new()
88            .subject("Welcome!")
89            .body("Hello, welcome to our service.")
90            .html("<h1>Hello!</h1>")
91            .from("noreply@example.com")
92            .cc("manager@example.com")
93            .bcc("archive@example.com");
94
95        assert_eq!(mail.subject, "Welcome!");
96        assert_eq!(mail.body, "Hello, welcome to our service.");
97        assert_eq!(mail.html, Some("<h1>Hello!</h1>".into()));
98        assert_eq!(mail.from, Some("noreply@example.com".into()));
99        assert_eq!(mail.cc, vec!["manager@example.com"]);
100        assert_eq!(mail.bcc, vec!["archive@example.com"]);
101    }
102}