1use core::fmt;
2use file::File;
3use itertools::Itertools;
4use serde::{Deserialize, Serialize};
5use std::fmt::Display;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct EmailAddress {
9 #[serde(default, skip_serializing_if = "Option::is_none")]
10 pub name: Option<String>,
11 pub address: String,
12}
13
14impl<T: Into<String>> From<T> for EmailAddress {
15 fn from(value: T) -> Self {
16 EmailAddress {
17 name: None,
18 address: value.into(),
19 }
20 }
21}
22
23impl Display for EmailAddress {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 if let Some(name) = &self.name {
26 write!(f, "{} <{}>", name, self.address)
27 } else {
28 write!(f, "{}", self.address)
29 }
30 }
31}
32
33pub enum Body {
34 Text(String),
35 Html(String),
36 Combined { text: String, html: String },
37}
38
39impl Default for Body {
40 fn default() -> Self {
41 Body::Text("".to_string())
42 }
43}
44
45impl fmt::Display for Body {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 match self {
48 Body::Text(text) => write!(f, "{}", text),
49 Body::Html(html) => write!(f, "{}", html),
50 Body::Combined { text, .. } => write!(f, "{text}"),
51 }
52 }
53}
54
55pub struct Email {
56 pub from: EmailAddress,
57 pub to: Vec<EmailAddress>,
58 pub cc: Vec<EmailAddress>,
59 pub bcc: Vec<EmailAddress>,
60 pub subject: String,
61 pub body: Body,
62 pub attachments: Vec<File>,
63 pub reply_to_message_id: Option<String>,
65 pub thread_id: Option<String>,
66}
67
68impl Email {
69 pub fn to_header(&self) -> String {
70 self.to
71 .iter()
72 .map(|x| x.to_string())
73 .collect_vec()
74 .join(", ")
75 }
76 pub fn cc_header(&self) -> String {
77 self.cc
78 .iter()
79 .map(|x| x.to_string())
80 .collect_vec()
81 .join(", ")
82 }
83 pub fn bcc_header(&self) -> String {
84 self.bcc
85 .iter()
86 .map(|x| x.to_string())
87 .collect_vec()
88 .join(", ")
89 }
90}