dust_mail/client/
builder.rs

1use std::{collections::HashMap, fmt::Display, result};
2
3use crate::error::{err, Error, ErrorKind, Result};
4
5use super::{address::Address, content::Content, incoming::types::flag::Flag, parser, Headers};
6
7#[derive(Debug)]
8pub struct MessageBuilder {
9    pub(crate) from: Option<Address>,
10    pub(crate) to: Option<Address>,
11    pub(crate) cc: Option<Address>,
12    pub(crate) bcc: Option<Address>,
13    pub(crate) flags: Vec<Flag>,
14    pub(crate) id: Option<String>,
15    pub(crate) sent: Option<i64>,
16    pub(crate) subject: Option<String>,
17    pub(crate) headers: Option<Headers>,
18    pub(crate) content: Content,
19}
20
21#[cfg(feature = "maildir")]
22impl TryFrom<maildir::MailEntry> for MessageBuilder {
23    type Error = Error;
24
25    fn try_from(mut mail_entry: maildir::MailEntry) -> result::Result<Self, Self::Error> {
26        let parsed = mail_entry.parsed()?;
27
28        let mut builder = parser::message::from_parsed_mail(parsed)?;
29
30        if mail_entry.is_seen() {
31            builder = builder.flag(Flag::Read);
32        }
33
34        if mail_entry.is_flagged() {
35            builder = builder.flag(Flag::Flagged);
36        }
37
38        if mail_entry.is_draft() {
39            builder = builder.flag(Flag::Draft);
40        }
41
42        if mail_entry.is_trashed() {
43            builder = builder.flag(Flag::Deleted);
44        }
45
46        if mail_entry.is_replied() {
47            builder = builder.flag(Flag::Answered);
48        }
49
50        Ok(builder)
51    }
52}
53
54impl TryFrom<&[u8]> for MessageBuilder {
55    type Error = Error;
56
57    fn try_from(bytes: &[u8]) -> result::Result<Self, Self::Error> {
58        parser::message::from_rfc822(bytes)
59    }
60}
61
62impl MessageBuilder {
63    pub fn new() -> Self {
64        Self {
65            flags: Vec::new(),
66            from: None,
67            bcc: None,
68            cc: None,
69            to: None,
70            id: None,
71            sent: None,
72            subject: None,
73            content: Content::default(),
74            headers: None,
75        }
76    }
77
78    pub fn flags<F: IntoIterator<Item = Flag>>(mut self, flags: F) -> Self {
79        let mut iter = flags.into_iter();
80
81        while let Some(flag) = iter.next() {
82            self.flags.push(flag)
83        }
84
85        self
86    }
87
88    pub fn flag(mut self, flag: Flag) -> Self {
89        self.flags.push(flag);
90
91        self
92    }
93
94    pub fn senders<C: Into<Address>>(mut self, sender: C) -> Self {
95        self.from = Some(sender.into());
96
97        self
98    }
99
100    pub fn recipients<C: Into<Address>>(mut self, recipient: C) -> Self {
101        self.to = Some(recipient.into());
102
103        self
104    }
105
106    pub fn cc<C: Into<Address>>(mut self, cc: C) -> Self {
107        self.cc = Some(cc.into());
108
109        self
110    }
111
112    pub fn bcc<C: Into<Address>>(mut self, bcc: C) -> Self {
113        self.bcc = Some(bcc.into());
114
115        self
116    }
117
118    pub fn id<I: Display>(mut self, id: I) -> Self {
119        self.id = Some(id.to_string());
120
121        self
122    }
123
124    pub fn sent(mut self, sent: i64) -> Self {
125        self.sent = Some(sent);
126
127        self
128    }
129
130    pub fn subject<S: Display>(mut self, subject: S) -> Self {
131        self.subject = Some(subject.to_string());
132
133        self
134    }
135
136    pub fn headers(mut self, headers: Headers) -> Self {
137        self.headers = Some(headers);
138
139        self
140    }
141
142    pub fn header<H: Into<String>, V: Display>(mut self, header: H, value: V) -> Self {
143        if let None = self.headers {
144            self.headers = Some(HashMap::new());
145        }
146
147        if let Some(headers) = self.headers.as_mut() {
148            headers.insert(header.into(), value.to_string());
149        }
150
151        self
152    }
153
154    pub fn html<H: Into<String>>(mut self, html: H) -> Self {
155        self.content.set_html(html);
156
157        self
158    }
159
160    pub fn text<H: Into<String>>(mut self, text: H) -> Self {
161        self.content.set_text(text);
162
163        self
164    }
165
166    pub fn build<T: TryFrom<Self>>(self) -> Result<T> {
167        match self.try_into() {
168            Ok(message) => Ok(message),
169            Err(_err) => err!(ErrorKind::InvalidMessage, "Could not build a valid message"),
170        }
171    }
172}