emailmessage/
message.rs

1use super::{Body, Mailbox};
2use bytes::{BufMut, Bytes, BytesMut, IntoBuf};
3use encoder::{EncoderError, EncoderStream};
4use futures::{Async, Poll, Stream};
5use header::{self, EmailDate, Header, Headers, MailboxesHeader};
6use hyper::body::Payload;
7use std::fmt::{Display, Formatter, Result as FmtResult};
8use std::mem::replace;
9use std::time::SystemTime;
10
11/// A builder for messages
12#[derive(Debug, Clone)]
13pub struct MessageBuilder {
14    headers: Headers,
15}
16
17impl MessageBuilder {
18    /// Creates a new default message builder
19    #[inline]
20    pub fn new() -> Self {
21        Self {
22            headers: Headers::new(),
23        }
24    }
25
26    /// Set custom header to message
27    #[inline]
28    pub fn header<H: Header>(mut self, header: H) -> Self {
29        self.headers.set(header);
30        self
31    }
32
33    /// Add mailbox to header
34    pub fn mailbox<H: Header + MailboxesHeader>(mut self, header: H) -> Self {
35        if self.headers.has::<H>() {
36            self.headers.get_mut::<H>().unwrap().join_mailboxes(header);
37            self
38        } else {
39            self.header(header)
40        }
41    }
42
43    /// Add `Date:` header to message
44    ///
45    /// Shortcut for `self.header(header::Date(date))`.
46    #[inline]
47    pub fn date(self, date: EmailDate) -> Self {
48        self.header(header::Date(date))
49    }
50
51    /// Set `Date:` header using current date/time
52    ///
53    /// Shortcut for `self.date(SystemTime::now())`.
54    #[inline]
55    pub fn date_now(self) -> Self {
56        self.date(SystemTime::now().into())
57    }
58
59    /// Set `Subject:` header to message
60    ///
61    /// Shortcut for `self.header(header::Subject(subject.into()))`.
62    #[inline]
63    pub fn subject<S: Into<String>>(self, subject: S) -> Self {
64        self.header(header::Subject(subject.into()))
65    }
66
67    /// Set `Mime-Version:` header to 1.0
68    ///
69    /// Shortcut for `self.header(header::MIME_VERSION_1_0)`.
70    #[inline]
71    pub fn mime_1_0(self) -> Self {
72        self.header(header::MIME_VERSION_1_0)
73    }
74
75    /// Set `Sender:` header
76    ///
77    /// Shortcut for `self.header(header::Sender(mbox))`.
78    #[inline]
79    pub fn sender(self, mbox: Mailbox) -> Self {
80        self.header(header::Sender(mbox))
81    }
82
83    /// Set or add mailbox to `From:` header
84    ///
85    /// Shortcut for `self.mailbox(header::From(mbox))`.
86    #[inline]
87    pub fn from(self, mbox: Mailbox) -> Self {
88        self.mailbox(header::From(mbox.into()))
89    }
90
91    /// Set or add mailbox to `ReplyTo:` header
92    ///
93    /// Shortcut for `self.mailbox(header::ReplyTo(mbox))`.
94    #[inline]
95    pub fn reply_to(self, mbox: Mailbox) -> Self {
96        self.mailbox(header::ReplyTo(mbox.into()))
97    }
98
99    /// Set or add mailbox to `To:` header
100    ///
101    /// Shortcut for `self.mailbox(header::To(mbox))`.
102    #[inline]
103    pub fn to(self, mbox: Mailbox) -> Self {
104        self.mailbox(header::To(mbox.into()))
105    }
106
107    /// Set or add mailbox to `Cc:` header
108    ///
109    /// Shortcut for `self.mailbox(header::Cc(mbox))`.
110    #[inline]
111    pub fn cc(self, mbox: Mailbox) -> Self {
112        self.mailbox(header::Cc(mbox.into()))
113    }
114
115    /// Set or add mailbox to `Bcc:` header
116    ///
117    /// Shortcut for `self.mailbox(header::Bcc(mbox))`.
118    #[inline]
119    pub fn bcc(self, mbox: Mailbox) -> Self {
120        self.mailbox(header::Bcc(mbox.into()))
121    }
122
123    /// Create message using body
124    #[inline]
125    pub fn body<T>(self, body: T) -> Message<T> {
126        Message {
127            headers: self.headers,
128            split: true,
129            body,
130        }
131    }
132
133    /// Create message by joining content
134    #[inline]
135    pub fn join<T>(self, body: T) -> Message<T> {
136        Message {
137            headers: self.headers,
138            split: false,
139            body,
140        }
141    }
142
143    /// Create message using mime body ([`MultiPart`](::MultiPart) or [`SinglePart`](::SinglePart))
144    ///
145    /// Shortcut for `self.mime_1_0().join(body)`.
146    #[inline]
147    pub fn mime_body<T>(self, body: T) -> Message<T> {
148        self.mime_1_0().join(body)
149    }
150}
151
152/// Email message which can be formatted or streamed
153#[derive(Clone, Debug)]
154pub struct Message<B = Body> {
155    headers: Headers,
156    split: bool,
157    body: B,
158}
159
160impl Message<()> {
161    /// Create a new message builder without headers
162    #[inline]
163    pub fn builder() -> MessageBuilder {
164        MessageBuilder::new()
165    }
166
167    /// Constructs a default message builder with date header which filled using current local time
168    #[inline]
169    pub fn create() -> MessageBuilder {
170        Self::builder().date_now()
171    }
172}
173
174impl<B> Message<B> {
175    /// Get the headers from the Message
176    #[inline]
177    pub fn headers(&self) -> &Headers {
178        &self.headers
179    }
180
181    /// Get a mutable reference to the headers
182    #[inline]
183    pub fn headers_mut(&mut self) -> &mut Headers {
184        &mut self.headers
185    }
186
187    /// Set the body
188    #[inline]
189    pub fn set_body<T: Into<B>>(&mut self, body: T) {
190        self.body = body.into();
191    }
192
193    /// Read the body
194    #[inline]
195    pub fn body_ref(&self) -> &B {
196        &self.body
197    }
198
199    /// Converts message into stream
200    pub fn into_stream(self) -> MessageStream<B>
201    where
202        B: Payload,
203    {
204        self.into()
205    }
206}
207
208/// Stream for message
209pub struct MessageStream<B> {
210    headers: Option<Headers>,
211    split: bool,
212    body: Option<EncoderStream<B>>,
213}
214
215impl<B> Stream for MessageStream<B>
216where
217    B: Payload,
218    B::Data: IntoBuf,
219{
220    type Item = Bytes;
221    type Error = EncoderError<B::Error>;
222
223    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
224        if self.headers.is_none() {
225            // stream body
226            let res = if let Some(body) = &mut self.body {
227                body.poll()
228            } else {
229                // end of data
230                return Ok(Async::Ready(None));
231            };
232
233            return if let Ok(Async::Ready(None)) = &res {
234                // end of stream
235                self.body = None;
236                Ok(Async::Ready(None))
237            } else {
238                // chunk or error
239                res
240            };
241        }
242
243        // stream headers
244        let headers = replace(&mut self.headers, None).unwrap().to_string();
245        let mut out = BytesMut::with_capacity(headers.len() + if self.split { 2 } else { 0 });
246        out.put(&headers);
247        if self.split {
248            out.put_slice(b"\r\n");
249        }
250        Ok(Async::Ready(Some(out.freeze())))
251    }
252}
253
254/// Convert message into boxed stream of binary chunks
255///
256impl<B> From<Message<B>> for MessageStream<B>
257where
258    B: Payload,
259{
260    fn from(
261        Message {
262            headers,
263            split,
264            body,
265        }: Message<B>,
266    ) -> Self {
267        let body = {
268            let encoding = headers.get();
269            EncoderStream::wrap(encoding, body)
270        };
271
272        MessageStream {
273            headers: Some(headers),
274            split,
275            body: Some(body),
276        }
277    }
278}
279
280impl Default for MessageBuilder {
281    fn default() -> Self {
282        MessageBuilder::new()
283    }
284}
285
286impl<B> Display for Message<B>
287where
288    B: Display,
289{
290    fn fmt(&self, f: &mut Formatter) -> FmtResult {
291        self.headers.fmt(f)?;
292        if self.split {
293            f.write_str("\r\n")?;
294        }
295        self.body.fmt(f)
296    }
297}
298
299#[cfg(test)]
300mod test {
301    use header;
302    use mailbox::Mailbox;
303    use message::Message;
304
305    use futures::{Future, Stream};
306    use std::str::from_utf8;
307
308    #[test]
309    fn date_header() {
310        let date = "Tue, 15 Nov 1994 08:12:31 GMT".parse().unwrap();
311
312        let email = Message::builder().date(date).body("");
313
314        assert_eq!(
315            format!("{}", email),
316            "Date: Tue, 15 Nov 1994 08:12:31 GMT\r\n\r\n"
317        );
318    }
319
320    #[test]
321    fn email_message() {
322        let date = "Tue, 15 Nov 1994 08:12:31 GMT".parse().unwrap();
323
324        let email = Message::builder()
325            .date(date)
326            .header(header::From(
327                vec![Mailbox::new(
328                    Some("Каи".into()),
329                    "kayo@example.com".parse().unwrap(),
330                )].into(),
331            )).header(header::To(
332                vec!["Pony O.P. <pony@domain.tld>".parse().unwrap()].into(),
333            )).header(header::Subject("яңа ел белән!".into()))
334            .body("Happy new year!");
335
336        assert_eq!(
337            format!("{}", email),
338            concat!(
339                "Date: Tue, 15 Nov 1994 08:12:31 GMT\r\n",
340                "From: =?utf-8?b?0JrQsNC4?= <kayo@example.com>\r\n",
341                "To: Pony O.P. <pony@domain.tld>\r\n",
342                "Subject: =?utf-8?b?0Y/So9CwINC10Lsg0LHQtdC705nQvSE=?=\r\n",
343                "\r\n",
344                "Happy new year!"
345            )
346        );
347    }
348
349    #[test]
350    fn message_to_stream() {
351        let date = "Tue, 15 Nov 1994 08:12:31 GMT".parse().unwrap();
352
353        let email: Message = Message::builder()
354            .date(date)
355            .header(header::From(
356                vec![Mailbox::new(
357                    Some("Каи".into()),
358                    "kayo@example.com".parse().unwrap(),
359                )].into(),
360            )).header(header::To(
361                vec!["Pony O.P. <pony@domain.tld>".parse().unwrap()].into(),
362            )).header(header::Subject("яңа ел белән!".into()))
363            .body("Happy new year!".into());
364
365        let body = email.into_stream();
366
367        assert_eq!(
368            body.concat2()
369                .map(|b| String::from(from_utf8(&b).unwrap()))
370                .wait()
371                .unwrap(),
372            concat!(
373                "Date: Tue, 15 Nov 1994 08:12:31 GMT\r\n",
374                "From: =?utf-8?b?0JrQsNC4?= <kayo@example.com>\r\n",
375                "To: Pony O.P. <pony@domain.tld>\r\n",
376                "Subject: =?utf-8?b?0Y/So9CwINC10Lsg0LHQtdC705nQvSE=?=\r\n",
377                "\r\n",
378                "Happy new year!"
379            )
380        );
381    }
382}