1use crate::{
4 datatypes::{DateTime, EmailAddress, EmailMessageId},
5 formatters::{
6 DisplayConcise, DisplayDetailed, DisplayInline, DisplayJsonLd, DisplayMime, DisplayOneliner,
7 },
8};
9use alloc::fmt;
10
11#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
13#[cfg_attr(feature = "serde", cfg_eval::cfg_eval, serde_with::serde_as)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[cfg_attr(feature = "serde", serde(default, rename_all = "camelCase"))]
16pub struct EmailMessage {
17 pub date: DateTime,
19
20 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))]
22 pub from: Vec<EmailAddress>,
23
24 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
26 pub sender: Option<EmailAddress>,
27
28 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))]
30 pub reply_to: Vec<EmailAddress>,
31
32 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))]
34 pub to: Vec<EmailAddress>,
35
36 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))]
38 pub cc: Vec<EmailAddress>,
39
40 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))]
42 pub bcc: Vec<EmailAddress>,
43
44 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
46 pub subject: Option<String>,
47
48 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
50 pub id: Option<EmailMessageId>,
51
52 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))]
54 pub in_reply_to: Vec<EmailMessageId>,
55
56 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))]
58 pub references: Vec<EmailMessageId>,
59
60 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
62 pub body: Option<String>,
63}
64
65impl EmailMessage {
66 pub fn inline(&self) -> DisplayInline<EmailMessage> {
67 DisplayInline(self)
68 }
69
70 pub fn oneliner(&self) -> DisplayOneliner<EmailMessage> {
71 DisplayOneliner(self)
72 }
73
74 pub fn concise(&self) -> DisplayConcise<EmailMessage> {
75 DisplayConcise(self)
76 }
77
78 pub fn detailed(&self) -> DisplayDetailed<EmailMessage> {
79 DisplayDetailed(self)
80 }
81
82 pub fn mime(&self) -> DisplayMime<EmailMessage> {
83 DisplayMime(self)
84 }
85
86 pub fn jsonld(&self) -> DisplayJsonLd<EmailMessage> {
87 DisplayJsonLd(self)
88 }
89}
90
91impl fmt::Display for EmailMessage {
92 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93 writeln!(f, "{}", self.oneliner())
94 }
95}
96
97impl fmt::Display for DisplayInline<'_, EmailMessage> {
98 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99 write!(f, "{}", self.0.date)?;
100 for addr in &self.0.from {
101 write!(f, " {}:", addr.as_str())?;
102 }
103 if let Some(ref subject) = self.0.subject {
104 write!(f, " {}", subject)?;
105 }
106 Ok(())
107 }
108}
109
110impl fmt::Display for DisplayOneliner<'_, EmailMessage> {
111 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112 writeln!(f, "{}", self.0.inline())
113 }
114}
115
116impl fmt::Display for DisplayConcise<'_, EmailMessage> {
117 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118 if let Some(ref subject) = self.0.subject {
119 writeln!(f, "✉️ {}", subject)?;
120 }
121 writeln!(f, "\tDate: {}", self.0.date.inline())?;
122 if !self.0.from.is_empty() {
123 writeln!(
124 f,
125 "\tFrom: {}",
126 &self
127 .0
128 .from
129 .iter()
130 .map(|addr| format!("{}", addr.inline()))
131 .collect::<Vec<String>>()
132 .join(", ")
133 )?;
134 }
135 if !self.0.to.is_empty() {
136 writeln!(
137 f,
138 "\tTo: {}",
139 &self
140 .0
141 .to
142 .iter()
143 .map(|addr| format!("{}", addr.inline()))
144 .collect::<Vec<String>>()
145 .join(", ")
146 )?;
147 }
148 if !self.0.cc.is_empty() {
149 writeln!(
150 f,
151 "\tCc: {}",
152 &self
153 .0
154 .cc
155 .iter()
156 .map(|addr| format!("{}", addr.inline()))
157 .collect::<Vec<String>>()
158 .join(", ")
159 )?;
160 }
161 if !self.0.bcc.is_empty() {
162 writeln!(
163 f,
164 "\tBcc: {}",
165 &self
166 .0
167 .bcc
168 .iter()
169 .map(|addr| format!("{}", addr.inline()))
170 .collect::<Vec<String>>()
171 .join(", ")
172 )?;
173 }
174 if let Some(ref id) = self.0.id {
175 writeln!(f, "\tMessage-ID: {}", id.inline())?;
176 }
177 Ok(())
178 }
179}
180
181impl fmt::Display for DisplayDetailed<'_, EmailMessage> {
182 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
183 if let Some(ref subject) = self.0.subject {
184 writeln!(f, "✉️ {}", subject)?;
185 }
186 writeln!(f, "\tDate: {}", self.0.date.inline())?;
187 for addr in &self.0.from {
188 writeln!(f, "\tFrom: {}", addr.inline())?;
189 }
190 for addr in &self.0.to {
191 writeln!(f, "\tTo: {}", addr.inline())?;
192 }
193 for addr in &self.0.cc {
194 writeln!(f, "\tCc: {}", addr.inline())?;
195 }
196 for addr in &self.0.bcc {
197 writeln!(f, "\tBcc: {}", addr.inline())?;
198 }
199 if let Some(ref id) = self.0.id {
200 writeln!(f, "\tMessage-ID: {}", id.inline())?;
201 }
202 for in_reply_to in &self.0.in_reply_to {
203 writeln!(f, "\tIn-Reply-To: {}", in_reply_to.inline())?;
204 }
205 for references in &self.0.references {
206 writeln!(f, "\tReferences: {}", references.inline())?;
207 }
208 Ok(())
209 }
210}
211
212impl fmt::Display for DisplayMime<'_, EmailMessage> {
213 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214 writeln!(f, "Date: {}", self.0.date.mime())?;
215 for addr in &self.0.from {
216 writeln!(f, "From: {}", addr.as_str())?;
217 }
218 for addr in &self.0.to {
219 writeln!(f, "To: {}", addr.as_str())?;
220 }
221 for addr in &self.0.cc {
222 writeln!(f, "Cc: {}", addr.as_str())?;
223 }
224 for addr in &self.0.bcc {
225 writeln!(f, "Bcc: {}", addr.as_str())?;
226 }
227 if let Some(ref subject) = self.0.subject {
228 writeln!(f, "Subject: {}", subject)?;
229 }
230 if let Some(ref id) = self.0.id {
231 writeln!(f, "Message-ID: <{}>", id.as_str())?;
232 }
233 for in_reply_to in &self.0.in_reply_to {
234 writeln!(f, "In-Reply-To: {}", in_reply_to.inline())?;
235 }
236 for references in &self.0.references {
237 writeln!(f, "References: {}", references.inline())?;
238 }
239 if let Some(ref body) = self.0.body {
240 writeln!(f)?;
241 writeln!(f, "{}", body)?;
242 }
243 Ok(())
244 }
245}
246
247#[cfg(feature = "tldr")]
248impl tldr::Tldr for EmailMessage {
249 type Error = Box<dyn core::error::Error>;
250
251 fn what(&self, ctx: &tldr::TldrContext) -> tldr::TldrResult<String, Self::Error> {
252 use core::fmt::Write;
253 use tldr::TldrLanguage::*;
254 Ok(match ctx.language {
255 English => {
256 let timespan = DateTime::now().since(&self.date)?.round()?;
257
258 let mut tldr = String::new();
259 write!(tldr, "An email message dated {timespan} ago")?;
260 if let Some(from) = &self.from.first() {
261 write!(tldr, ", from {}", from)?;
262 }
263 if !self.to.is_empty() {
264 write!(tldr, ", addressed to ")?;
265 for (i, addr) in self.to.iter().enumerate() {
266 if i > 0 {
267 write!(tldr, ", ")?;
268 }
269 write!(tldr, "{}", addr)?;
270 }
271 }
272 if let Some(subject) = &self.subject {
273 write!(tldr, ", with the subject \"{}\"", subject)?;
274 }
275 Some(tldr)
276 },
277 _ => None,
278 })
279 }
280}
281
282#[cfg(feature = "imap-proto")]
283include!("email_message/imap_proto.rs");
284
285#[cfg(feature = "mail-parser")]
286include!("email_message/mail_parser.rs");
287
288#[cfg(feature = "maildir")]
289include!("email_message/maildir.rs");
290
291#[cfg(feature = "mailparse")]
292include!("email_message/mailparse.rs");
293
294#[cfg(feature = "serde")]
295include!("email_message/serde.rs");