lettre/message/header/
mailbox.rs

1use email_encoding::headers::writer::EmailWriter;
2
3use super::{Header, HeaderName, HeaderValue};
4use crate::{
5    message::mailbox::{Mailbox, Mailboxes},
6    BoxError,
7};
8
9/// Header which can contains multiple mailboxes
10pub trait MailboxesHeader {
11    fn join_mailboxes(&mut self, other: Self);
12}
13
14macro_rules! mailbox_header {
15    ($(#[$doc:meta])*($type_name: ident, $header_name: expr)) => {
16        $(#[$doc])*
17        #[derive(Debug, Clone, PartialEq, Eq)]
18        pub struct $type_name(Mailbox);
19
20        impl Header for $type_name {
21            fn name() -> HeaderName {
22                HeaderName::new_from_ascii_str($header_name)
23            }
24
25            fn parse(s: &str) -> Result<Self, BoxError> {
26                let mailbox: Mailbox = s.parse()?;
27                Ok(Self(mailbox))
28            }
29
30            fn display(&self) -> HeaderValue {
31                let mut encoded_value = String::new();
32                let line_len = $header_name.len() + ": ".len();
33                {
34                    let mut w = EmailWriter::new(&mut encoded_value, line_len, 0, false);
35                    self.0.encode(&mut w).expect("writing `Mailbox` returned an error");
36                }
37
38                HeaderValue::dangerous_new_pre_encoded(Self::name(), self.0.to_string(), encoded_value)
39            }
40        }
41
42        impl std::convert::From<Mailbox> for $type_name {
43            #[inline]
44            fn from(mailbox: Mailbox) -> Self {
45                Self(mailbox)
46            }
47        }
48
49        impl std::convert::From<$type_name> for Mailbox {
50            #[inline]
51            fn from(this: $type_name) -> Mailbox {
52                this.0
53            }
54        }
55    };
56}
57
58macro_rules! mailboxes_header {
59    ($(#[$doc:meta])*($type_name: ident, $header_name: expr)) => {
60        $(#[$doc])*
61        #[derive(Debug, Clone, PartialEq, Eq)]
62        pub struct $type_name(pub(crate) Mailboxes);
63
64        impl MailboxesHeader for $type_name {
65            fn join_mailboxes(&mut self, other: Self) {
66                self.0.extend(other.0);
67            }
68        }
69
70        impl Header for $type_name {
71            fn name() -> HeaderName {
72                HeaderName::new_from_ascii_str($header_name)
73            }
74
75            fn parse(s: &str) -> Result<Self, BoxError> {
76                let mailbox: Mailboxes = s.parse()?;
77                Ok(Self(mailbox))
78            }
79
80            fn display(&self) -> HeaderValue {
81                let mut encoded_value = String::new();
82                let line_len = $header_name.len() + ": ".len();
83                {
84                    let mut w = EmailWriter::new(&mut encoded_value, line_len, 0, false);
85                    self.0.encode(&mut w).expect("writing `Mailboxes` returned an error");
86                }
87
88                HeaderValue::dangerous_new_pre_encoded(Self::name(), self.0.to_string(), encoded_value)
89            }
90        }
91
92        impl std::convert::From<Mailboxes> for $type_name {
93            #[inline]
94            fn from(mailboxes: Mailboxes) -> Self {
95                Self(mailboxes)
96            }
97        }
98
99        impl std::convert::From<$type_name> for Mailboxes {
100            #[inline]
101            fn from(this: $type_name) -> Mailboxes {
102                this.0
103            }
104        }
105    };
106}
107
108mailbox_header! {
109    /**
110
111    `Sender` header
112
113    This header contains [`Mailbox`] associated with sender.
114
115    ```no_test
116    header::Sender("Mr. Sender <sender@example.com>".parse().unwrap())
117    ```
118     */
119    (Sender, "Sender")
120}
121
122mailboxes_header! {
123    /**
124
125    `From` header
126
127    This header contains [`Mailboxes`].
128
129     */
130    (From, "From")
131}
132
133mailboxes_header! {
134    /**
135
136    `Reply-To` header
137
138    This header contains [`Mailboxes`].
139
140     */
141    (ReplyTo, "Reply-To")
142}
143
144mailboxes_header! {
145    /**
146
147    `To` header
148
149    This header contains [`Mailboxes`].
150
151     */
152    (To, "To")
153}
154
155mailboxes_header! {
156    /**
157
158    `Cc` header
159
160    This header contains [`Mailboxes`].
161
162     */
163    (Cc, "Cc")
164}
165
166mailboxes_header! {
167    /**
168
169    `Bcc` header
170
171    This header contains [`Mailboxes`].
172
173     */
174    (Bcc, "Bcc")
175}
176
177#[cfg(test)]
178mod test {
179    use pretty_assertions::assert_eq;
180
181    use super::{From, Mailbox, Mailboxes};
182    use crate::message::header::{HeaderName, HeaderValue, Headers};
183
184    #[test]
185    fn format_single_without_name() {
186        let from = Mailboxes::new().with("kayo@example.com".parse().unwrap());
187
188        let mut headers = Headers::new();
189        headers.set(From(from));
190
191        assert_eq!(headers.to_string(), "From: kayo@example.com\r\n");
192    }
193
194    #[test]
195    fn format_single_with_name() {
196        let from = Mailboxes::new().with("Kayo <kayo@example.com>".parse().unwrap());
197
198        let mut headers = Headers::new();
199        headers.set(From(from));
200
201        assert_eq!(headers.to_string(), "From: Kayo <kayo@example.com>\r\n");
202    }
203
204    #[test]
205    fn format_multi_without_name() {
206        let from = Mailboxes::new()
207            .with("kayo@example.com".parse().unwrap())
208            .with("pony@domain.tld".parse().unwrap());
209
210        let mut headers = Headers::new();
211        headers.set(From(from));
212
213        assert_eq!(
214            headers.to_string(),
215            "From: kayo@example.com, pony@domain.tld\r\n"
216        );
217    }
218
219    #[test]
220    fn format_multi_with_name() {
221        let from = vec![
222            "Kayo <kayo@example.com>".parse().unwrap(),
223            "Pony P. <pony@domain.tld>".parse().unwrap(),
224        ];
225
226        let mut headers = Headers::new();
227        headers.set(From(from.into()));
228
229        assert_eq!(
230            headers.to_string(),
231            "From: Kayo <kayo@example.com>, \"Pony P.\" <pony@domain.tld>\r\n"
232        );
233    }
234
235    #[test]
236    fn format_single_with_utf8_name() {
237        let from = vec!["Кайо <kayo@example.com>".parse().unwrap()];
238
239        let mut headers = Headers::new();
240        headers.set(From(from.into()));
241
242        assert_eq!(
243            headers.to_string(),
244            "From: =?utf-8?b?0JrQsNC50L4=?= <kayo@example.com>\r\n"
245        );
246    }
247
248    #[test]
249    fn parse_single_without_name() {
250        let from = vec!["kayo@example.com".parse().unwrap()].into();
251
252        let mut headers = Headers::new();
253        headers.insert_raw(HeaderValue::new(
254            HeaderName::new_from_ascii_str("From"),
255            "kayo@example.com".to_owned(),
256        ));
257
258        assert_eq!(headers.get::<From>(), Some(From(from)));
259    }
260
261    #[test]
262    fn parse_single_with_name() {
263        let from = vec!["K. <kayo@example.com>".parse().unwrap()].into();
264
265        let mut headers = Headers::new();
266        headers.insert_raw(HeaderValue::new(
267            HeaderName::new_from_ascii_str("From"),
268            "K. <kayo@example.com>".to_owned(),
269        ));
270
271        assert_eq!(headers.get::<From>(), Some(From(from)));
272    }
273
274    #[test]
275    fn parse_multi_without_name() {
276        let from: Vec<Mailbox> = vec![
277            "kayo@example.com".parse().unwrap(),
278            "pony@domain.tld".parse().unwrap(),
279        ];
280
281        let mut headers = Headers::new();
282        headers.insert_raw(HeaderValue::new(
283            HeaderName::new_from_ascii_str("From"),
284            "kayo@example.com, pony@domain.tld".to_owned(),
285        ));
286
287        assert_eq!(headers.get::<From>(), Some(From(from.into())));
288    }
289
290    #[test]
291    fn parse_multi_with_name() {
292        let from: Vec<Mailbox> = vec![
293            "K. <kayo@example.com>".parse().unwrap(),
294            "Pony P. <pony@domain.tld>".parse().unwrap(),
295        ];
296
297        let mut headers = Headers::new();
298        headers.insert_raw(HeaderValue::new(
299            HeaderName::new_from_ascii_str("From"),
300            "K. <kayo@example.com>, Pony P. <pony@domain.tld>".to_owned(),
301        ));
302
303        assert_eq!(headers.get::<From>(), Some(From(from.into())));
304    }
305
306    #[test]
307    fn parse_multi_with_name_containing_comma() {
308        let from: Vec<Mailbox> = vec![
309            "\"Test, test\" <1@example.com>".parse().unwrap(),
310            "\"Test2, test2\" <2@example.com>".parse().unwrap(),
311        ];
312
313        let mut headers = Headers::new();
314        headers.insert_raw(HeaderValue::new(
315            HeaderName::new_from_ascii_str("From"),
316            "\"Test, test\" <1@example.com>, \"Test2, test2\" <2@example.com>".to_owned(),
317        ));
318
319        assert_eq!(headers.get::<From>(), Some(From(from.into())));
320    }
321
322    #[test]
323    fn parse_multi_with_name_containing_double_quotes() {
324        let from: Vec<Mailbox> = vec![
325            "\"Test, test\" <1@example.com>".parse().unwrap(),
326            "\"Test2, \"test2\"\" <2@example.com>".parse().unwrap(),
327        ];
328
329        let mut headers = Headers::new();
330        headers.insert_raw(HeaderValue::new(
331            HeaderName::new_from_ascii_str("From"),
332            "\"Test, test\" <1@example.com>, \"Test2, \"test2\"\" <2@example.com>".to_owned(),
333        ));
334
335        assert_eq!(headers.get::<From>(), Some(From(from.into())));
336    }
337
338    #[test]
339    fn parse_multi_with_name_containing_comma_last_broken() {
340        let mut headers = Headers::new();
341        headers.insert_raw(HeaderValue::new(
342            HeaderName::new_from_ascii_str("From"),
343            "\"Test, test\" <1@example.com>, \"Test2, test2\"".to_owned(),
344        ));
345
346        assert_eq!(headers.get::<From>(), None);
347    }
348
349    #[test]
350    fn mailbox_format_address_with_angle_bracket() {
351        assert_eq!(
352            format!(
353                "{}",
354                Mailbox::new(Some("<3".into()), "i@love.example".parse().unwrap())
355            ),
356            r#""<3" <i@love.example>"#
357        );
358    }
359}