email_encoding/headers/
writer.rs1use core::fmt::{self, Write};
6
7use super::MAX_LINE_LEN;
8
9pub struct EmailWriter<'a> {
14 writer: &'a mut dyn Write,
15 line_len: usize,
16 spaces: usize,
17 can_go_to_new_line_now: bool,
18}
19
20impl<'a> EmailWriter<'a> {
21 pub fn new(
29 writer: &'a mut dyn Write,
30 line_len: usize,
31 spaces: usize,
32 can_go_to_new_line_now: bool,
33 ) -> Self {
34 Self {
35 writer,
36 line_len,
37 spaces,
38 can_go_to_new_line_now,
39 }
40 }
41
42 pub fn new_line(&mut self) -> fmt::Result {
44 self.writer.write_str("\r\n")?;
45 self.line_len = 0;
46 self.can_go_to_new_line_now = false;
47
48 Ok(())
49 }
50
51 pub fn space(&mut self) {
53 self.spaces += 1;
54 }
55
56 pub(super) fn forget_spaces(&mut self) {
58 self.spaces = 0;
59 }
60
61 pub(super) fn has_spaces(&mut self) -> bool {
62 self.spaces >= 1
63 }
64
65 pub fn line_len(&self) -> usize {
67 self.line_len
68 }
69
70 pub fn projected_line_len(&self) -> usize {
73 self.line_len + self.spaces
74 }
75
76 pub fn folding<'b>(&'b mut self) -> FoldingEmailWriter<'a, 'b> {
80 FoldingEmailWriter { writer: self }
81 }
82
83 fn write_spaces(&mut self) -> fmt::Result {
84 while self.spaces > 0 {
85 self.writer.write_char(' ')?;
86 self.line_len += 1;
87 self.spaces -= 1;
88 }
89
90 Ok(())
91 }
92}
93
94impl Write for EmailWriter<'_> {
95 fn write_str(&mut self, s: &str) -> fmt::Result {
96 self.write_spaces()?;
97
98 let s_after = s.trim_end_matches(' ');
99 self.spaces += s.len() - s_after.len();
100
101 if !s_after.is_empty() {
102 self.writer.write_str(s_after)?;
103 self.line_len += s_after.len();
104 self.can_go_to_new_line_now = true;
105 }
106
107 Ok(())
108 }
109
110 fn write_char(&mut self, c: char) -> fmt::Result {
111 if c == ' ' {
112 self.spaces += 1;
113 } else {
114 self.write_spaces()?;
115 self.can_go_to_new_line_now = true;
116
117 self.writer.write_char(c)?;
118 self.line_len += c.len_utf8();
119 }
120
121 Ok(())
122 }
123}
124
125impl Drop for EmailWriter<'_> {
126 fn drop(&mut self) {
127 let _ = self.write_spaces();
128 }
129}
130
131pub struct FoldingEmailWriter<'a, 'b> {
136 writer: &'b mut EmailWriter<'a>,
137}
138
139impl Write for FoldingEmailWriter<'_, '_> {
140 fn write_str(&mut self, mut s: &str) -> fmt::Result {
141 while !s.is_empty() {
142 if s.starts_with(' ') {
143 self.writer.space();
144 s = &s[1..];
145 continue;
146 }
147
148 let (start, end) = s.find(' ').map_or((s, ""), |i| s.split_at(i));
149
150 if self.writer.can_go_to_new_line_now
151 && self.writer.spaces >= 1
152 && (self.writer.projected_line_len() + start.len()) > MAX_LINE_LEN
153 {
154 self.writer.new_line()?;
155 }
156
157 self.writer.write_str(start)?;
158 s = end;
159 }
160
161 Ok(())
162 }
163
164 fn write_char(&mut self, c: char) -> fmt::Result {
165 if c == ' ' {
166 self.writer.spaces += 1;
167 } else {
168 self.write_str(c.encode_utf8(&mut [0u8; 4]))?;
169 }
170
171 Ok(())
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use alloc::borrow::ToOwned;
178
179 use pretty_assertions::assert_eq;
180
181 use super::*;
182
183 #[test]
184 fn wrap_immediate() {
185 let mut s =
186 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_owned();
187 let line_len = s.len();
188
189 {
190 let mut w = EmailWriter::new(&mut s, line_len, 0, true);
191 for _ in 0..16 {
192 w.folding().write_str("0123456789").unwrap();
193 }
194 }
195
196 assert_eq!(
197 s,
198 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789",
199 );
200 }
201
202 #[test]
203 fn wrap_keeping_final_whitespace() {
204 let mut s = "Subject: AAAAAAAAAAAAAA".to_owned();
205 let line_len = s.len();
206
207 {
208 let mut w = EmailWriter::new(&mut s, line_len, 1, true);
209 w.folding().write_str("12345 ").unwrap();
210 w.new_line().unwrap();
211 w.folding().write_str("12345").unwrap();
212 }
213
214 assert_eq!(s, concat!("Subject: AAAAAAAAAAAAAA 12345\r\n", " 12345"));
215 }
216
217 #[test]
218 fn catch_space() {
219 let mut s = "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_owned();
220 let line_len = s.len();
221
222 {
223 let mut w = EmailWriter::new(&mut s, line_len, 1, true);
224 w.folding().write_str("BBB ").unwrap();
225 w.folding().write_str("CCCCCCCCCCCCC").unwrap();
226 }
227
228 assert_eq!(
229 s,
230 concat!(
231 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBB\r\n",
232 " CCCCCCCCCCCCC"
233 )
234 );
235 }
236
237 #[test]
238 fn catch_spaces() {
239 let mut s = "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_owned();
240 let line_len = s.len();
241
242 {
243 let mut w = EmailWriter::new(&mut s, line_len, 1, true);
244 w.folding().write_str("BBB ").unwrap();
245 w.folding().write_str("CCCCCCCCCCCCC").unwrap();
246 }
247
248 assert_eq!(
249 s,
250 concat!(
251 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBB\r\n",
252 " CCCCCCCCCCCCC"
253 )
254 );
255 }
256
257 #[test]
258 fn explicit_space() {
259 let mut s = "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_owned();
260 let line_len = s.len();
261
262 {
263 let mut w = EmailWriter::new(&mut s, line_len, 1, true);
264 w.folding().write_str("BBB").unwrap();
265 w.space();
266 w.folding().write_str("CCCCCCCCCCCCC").unwrap();
267 }
268
269 assert_eq!(
270 s,
271 concat!(
272 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBB\r\n",
273 " CCCCCCCCCCCCC"
274 )
275 );
276 }
277
278 #[test]
279 fn explicit_spaces() {
280 let mut s = "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_owned();
281 let line_len = s.len();
282
283 {
284 let mut w = EmailWriter::new(&mut s, line_len, 1, true);
285 w.folding().write_str("BBB").unwrap();
286 w.space();
287 w.write_char(' ').unwrap();
288 w.space();
289 w.folding().write_str("CCCCCCCCCCCCC").unwrap();
290 }
291
292 assert_eq!(
293 s,
294 concat!(
295 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBB\r\n",
296 " CCCCCCCCCCCCC"
297 )
298 );
299 }
300
301 #[test]
302 fn optional_breakpoint() {
303 let mut s = "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
304 .to_owned();
305 let line_len = s.len();
306
307 {
308 let mut w = EmailWriter::new(&mut s, line_len, 0, true);
309 w.space();
310 w.folding().write_str("BBBBBBBBBB").unwrap();
311 w.space();
312 w.folding().write_str("CCCCCCCCCC").unwrap();
313 }
314
315 assert_eq!(
316 s,
317 concat!(
318 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n",
319 " BBBBBBBBBB CCCCCCCCCC",
320 )
321 );
322 }
323
324 #[test]
325 fn double_spaces_issue_949() {
326 let mut s = "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ".to_owned();
327 let line_len = s.len();
328
329 {
330 let mut w = EmailWriter::new(&mut s, line_len, 0, true);
331 w.folding().write_str("BBBBBBBBBBBBB ").unwrap();
332 crate::headers::rfc2047::encode("sélection", &mut w).unwrap();
333 }
334
335 assert_eq!(
336 s,
337 concat!(
338 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBB\r\n",
339 " =?utf-8?b?c8OpbGVjdGlvbg==?=",
340 )
341 );
342 }
343
344 #[test]
345 fn double_spaces_issue_949_no_space() {
346 let mut s = "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ".to_owned();
347 let line_len = s.len();
348
349 {
350 let mut w = EmailWriter::new(&mut s, line_len, 0, true);
351 w.folding().write_str("BBBBBBBBBBBBBBB").unwrap();
352 crate::headers::rfc2047::encode("sélection", &mut w).unwrap();
353 }
354
355 assert_eq!(
356 s,
357 concat!(
358 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBB=?utf-8?b?cw==?=\r\n",
359 " =?utf-8?b?w6lsZWN0aW9u?=",
360 )
361 );
362 }
363}