1use std::collections::HashMap;
2
3pub struct RenderedEmail {
9 pub subject: String,
11 pub html: String,
13 pub text: String,
15}
16
17pub struct SenderProfile {
23 pub from_name: String,
25 pub from_email: String,
27 pub reply_to: Option<String>,
29}
30
31pub struct SendEmail {
36 pub template: String,
38 pub to: Vec<String>,
40 pub cc: Vec<String>,
42 pub bcc: Vec<String>,
44 pub locale: Option<String>,
46 pub vars: HashMap<String, String>,
48 pub sender: Option<SenderProfile>,
50}
51
52impl SendEmail {
53 pub fn new(template: impl Into<String>, to: impl Into<String>) -> Self {
58 Self {
59 template: template.into(),
60 to: vec![to.into()],
61 cc: Vec::new(),
62 bcc: Vec::new(),
63 locale: None,
64 vars: HashMap::new(),
65 sender: None,
66 }
67 }
68
69 pub fn to(mut self, addr: impl Into<String>) -> Self {
71 self.to.push(addr.into());
72 self
73 }
74
75 pub fn cc(mut self, addr: impl Into<String>) -> Self {
77 self.cc.push(addr.into());
78 self
79 }
80
81 pub fn bcc(mut self, addr: impl Into<String>) -> Self {
83 self.bcc.push(addr.into());
84 self
85 }
86
87 pub fn locale(mut self, locale: impl Into<String>) -> Self {
89 self.locale = Some(locale.into());
90 self
91 }
92
93 pub fn var(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
98 self.vars.insert(key.into(), value.into());
99 self
100 }
101
102 pub fn sender(mut self, profile: SenderProfile) -> Self {
104 self.sender = Some(profile);
105 self
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn new_sets_template_and_first_recipient() {
115 let email = SendEmail::new("welcome", "user@example.com");
116 assert_eq!(email.template, "welcome");
117 assert_eq!(email.to, vec!["user@example.com"]);
118 assert!(email.cc.is_empty());
119 assert!(email.bcc.is_empty());
120 assert!(email.locale.is_none());
121 assert!(email.vars.is_empty());
122 assert!(email.sender.is_none());
123 }
124
125 #[test]
126 fn builder_chain() {
127 let email = SendEmail::new("reset", "a@example.com")
128 .to("b@example.com")
129 .cc("c@example.com")
130 .bcc("d@example.com")
131 .locale("uk")
132 .var("name", "Dmytro")
133 .var("token", "abc123")
134 .sender(SenderProfile {
135 from_name: "Support".into(),
136 from_email: "support@app.com".into(),
137 reply_to: Some("help@app.com".into()),
138 });
139 assert_eq!(email.to, vec!["a@example.com", "b@example.com"]);
140 assert_eq!(email.cc, vec!["c@example.com"]);
141 assert_eq!(email.bcc, vec!["d@example.com"]);
142 assert_eq!(email.locale.as_deref(), Some("uk"));
143 assert_eq!(email.vars.get("name").unwrap(), "Dmytro");
144 assert_eq!(email.vars.get("token").unwrap(), "abc123");
145 let sender = email.sender.unwrap();
146 assert_eq!(sender.from_name, "Support");
147 assert_eq!(sender.from_email, "support@app.com");
148 assert_eq!(sender.reply_to.as_deref(), Some("help@app.com"));
149 }
150
151 #[test]
152 fn var_overwrites_previous_value() {
153 let email = SendEmail::new("t", "a@b.com")
154 .var("key", "old")
155 .var("key", "new");
156 assert_eq!(email.vars.get("key").unwrap(), "new");
157 }
158}