mail_builder/headers/
address.rs1use std::borrow::Cow;
13
14use super::Header;
15use crate::encoders::encode::rfc2047_encode;
16
17#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
19pub struct EmailAddress<'x> {
20 pub name: Option<Cow<'x, str>>,
21 pub email: Cow<'x, str>,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
26pub struct GroupedAddresses<'x> {
27 pub name: Option<Cow<'x, str>>,
28 pub addresses: Vec<Address<'x>>,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
33pub enum Address<'x> {
34 Address(EmailAddress<'x>),
35 Group(GroupedAddresses<'x>),
36 List(Vec<Address<'x>>),
37}
38
39impl<'x> Address<'x> {
40 pub fn new_address(name: Option<impl Into<Cow<'x, str>>>, email: impl Into<Cow<'x, str>>) -> Self {
42 Address::Address(EmailAddress {
43 name: name.map(|v| v.into()),
44 email: email.into(),
45 })
46 }
47
48 pub fn new_group(name: Option<impl Into<Cow<'x, str>>>, addresses: Vec<Address<'x>>) -> Self {
50 Address::Group(GroupedAddresses {
51 name: name.map(|v| v.into()),
52 addresses,
53 })
54 }
55
56 pub fn new_list(items: Vec<Address<'x>>) -> Self {
58 Address::List(items)
59 }
60
61 pub fn unwrap_address(&self) -> &EmailAddress<'x> {
62 match self {
63 Address::Address(address) => address,
64 _ => panic!("Address is not an EmailAddress"),
65 }
66 }
67}
68
69impl<'x> From<(&'x str, &'x str)> for Address<'x> {
70 fn from(value: (&'x str, &'x str)) -> Self {
71 Address::Address(EmailAddress {
72 name: Some(value.0.into()),
73 email: value.1.into(),
74 })
75 }
76}
77
78impl<'x> From<(String, String)> for Address<'x> {
79 fn from(value: (String, String)) -> Self {
80 Address::Address(EmailAddress {
81 name: Some(value.0.into()),
82 email: value.1.into(),
83 })
84 }
85}
86
87impl<'x> From<&'x str> for Address<'x> {
88 fn from(value: &'x str) -> Self {
89 Address::Address(EmailAddress {
90 name: None,
91 email: value.into(),
92 })
93 }
94}
95
96impl<'x> From<String> for Address<'x> {
97 fn from(value: String) -> Self {
98 Address::Address(EmailAddress {
99 name: None,
100 email: value.into(),
101 })
102 }
103}
104
105impl<'x, T> From<Vec<T>> for Address<'x>
106where
107 T: Into<Address<'x>>,
108{
109 fn from(value: Vec<T>) -> Self {
110 Address::new_list(value.into_iter().map(|x| x.into()).collect())
111 }
112}
113
114impl<'x, T, U> From<(U, Vec<T>)> for Address<'x>
115where
116 T: Into<Address<'x>>,
117 U: Into<Cow<'x, str>>,
118{
119 fn from(value: (U, Vec<T>)) -> Self {
120 Address::Group(GroupedAddresses {
121 name: Some(value.0.into()),
122 addresses: value.1.into_iter().map(|x| x.into()).collect(),
123 })
124 }
125}
126
127impl<'x> Header for Address<'x> {
128 fn write_header(&self, mut output: impl std::io::Write, mut bytes_written: usize) -> std::io::Result<usize> {
129 match self {
130 Address::Address(address) => {
131 address.write_header(&mut output, bytes_written)?;
132 }
133 Address::Group(group) => {
134 group.write_header(&mut output, bytes_written)?;
135 }
136 Address::List(list) => {
137 for (pos, address) in list.iter().enumerate() {
138 if bytes_written
139 + (match address {
140 Address::Address(address) => {
141 address.email.len() + address.name.as_ref().map_or(0, |n| n.len() + 3) + 2
142 }
143 Address::Group(group) => group.name.as_ref().map_or(0, |name| name.len() + 2),
144 Address::List(_) => 0,
145 })
146 >= 76
147 {
148 output.write_all(b"\r\n\t")?;
149 bytes_written = 1;
150 }
151
152 match address {
153 Address::Address(address) => {
154 bytes_written += address.write_header(&mut output, bytes_written)?;
155 if pos < list.len() - 1 {
156 output.write_all(b", ")?;
157 bytes_written += 1;
158 }
159 }
160 Address::Group(group) => {
161 bytes_written += group.write_header(&mut output, bytes_written)?;
162 if pos < list.len() - 1 {
163 output.write_all(b"; ")?;
164 bytes_written += 1;
165 }
166 }
167 Address::List(_) => unreachable!(),
168 }
169 }
170 }
171 }
172 output.write_all(b"\r\n")?;
173 Ok(0)
174 }
175}
176
177impl<'x> Header for EmailAddress<'x> {
178 fn write_header(&self, mut output: impl std::io::Write, mut bytes_written: usize) -> std::io::Result<usize> {
179 if let Some(name) = &self.name {
180 bytes_written += rfc2047_encode(name, &mut output)?;
181 if bytes_written + self.email.len() + 2 >= 76 {
182 output.write_all(b"\r\n\t")?;
183 bytes_written = 1;
184 } else {
185 output.write_all(b" ")?;
186 bytes_written += 1;
187 }
188 }
189
190 output.write_all(b"<")?;
191 output.write_all(self.email.as_bytes())?;
192 output.write_all(b">")?;
193
194 Ok(bytes_written + self.email.len() + 2)
195 }
196}
197
198impl<'x> Header for GroupedAddresses<'x> {
199 fn write_header(&self, mut output: impl std::io::Write, mut bytes_written: usize) -> std::io::Result<usize> {
200 if let Some(name) = &self.name {
201 bytes_written += rfc2047_encode(name, &mut output)? + 2;
202 output.write_all(b": ")?;
203 }
204
205 for (pos, address) in self.addresses.iter().enumerate() {
206 let address = address.unwrap_address();
207
208 if bytes_written + address.email.len() + address.name.as_ref().map_or(0, |n| n.len() + 3) + 2 >= 76 {
209 output.write_all(b"\r\n\t")?;
210 bytes_written = 1;
211 }
212
213 bytes_written += address.write_header(&mut output, bytes_written)?;
214 if pos < self.addresses.len() - 1 {
215 output.write_all(b", ")?;
216 bytes_written += 2;
217 }
218 }
219
220 Ok(bytes_written)
221 }
222}