email_encoding/headers/
writer.rs1use std::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<'a> Write for EmailWriter<'a> {
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<'a> Drop for EmailWriter<'a> {
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<'a, 'b> Write for FoldingEmailWriter<'a, 'b> {
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 pretty_assertions::assert_eq;
178
179 use super::*;
180
181 #[test]
182 fn wrap_immediate() {
183 let mut s =
184 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_owned();
185 let line_len = s.len();
186
187 {
188 let mut w = EmailWriter::new(&mut s, line_len, 0, true);
189 for _ in 0..16 {
190 w.folding().write_str("0123456789").unwrap();
191 }
192 }
193
194 assert_eq!(
195 s,
196 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789",
197 );
198 }
199
200 #[test]
201 fn wrap_keeping_final_whitespace() {
202 let mut s = "Subject: AAAAAAAAAAAAAA".to_owned();
203 let line_len = s.len();
204
205 {
206 let mut w = EmailWriter::new(&mut s, line_len, 1, true);
207 w.folding().write_str("12345 ").unwrap();
208 w.new_line().unwrap();
209 w.folding().write_str("12345").unwrap();
210 }
211
212 assert_eq!(s, concat!("Subject: AAAAAAAAAAAAAA 12345\r\n", " 12345"));
213 }
214
215 #[test]
216 fn catch_space() {
217 let mut s = "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_owned();
218 let line_len = s.len();
219
220 {
221 let mut w = EmailWriter::new(&mut s, line_len, 1, true);
222 w.folding().write_str("BBB ").unwrap();
223 w.folding().write_str("CCCCCCCCCCCCC").unwrap();
224 }
225
226 assert_eq!(
227 s,
228 concat!(
229 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBB\r\n",
230 " CCCCCCCCCCCCC"
231 )
232 );
233 }
234
235 #[test]
236 fn catch_spaces() {
237 let mut s = "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_owned();
238 let line_len = s.len();
239
240 {
241 let mut w = EmailWriter::new(&mut s, line_len, 1, true);
242 w.folding().write_str("BBB ").unwrap();
243 w.folding().write_str("CCCCCCCCCCCCC").unwrap();
244 }
245
246 assert_eq!(
247 s,
248 concat!(
249 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBB\r\n",
250 " CCCCCCCCCCCCC"
251 )
252 );
253 }
254
255 #[test]
256 fn explicit_space() {
257 let mut s = "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_owned();
258 let line_len = s.len();
259
260 {
261 let mut w = EmailWriter::new(&mut s, line_len, 1, true);
262 w.folding().write_str("BBB").unwrap();
263 w.space();
264 w.folding().write_str("CCCCCCCCCCCCC").unwrap();
265 }
266
267 assert_eq!(
268 s,
269 concat!(
270 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBB\r\n",
271 " CCCCCCCCCCCCC"
272 )
273 );
274 }
275
276 #[test]
277 fn explicit_spaces() {
278 let mut s = "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_owned();
279 let line_len = s.len();
280
281 {
282 let mut w = EmailWriter::new(&mut s, line_len, 1, true);
283 w.folding().write_str("BBB").unwrap();
284 w.space();
285 w.write_char(' ').unwrap();
286 w.space();
287 w.folding().write_str("CCCCCCCCCCCCC").unwrap();
288 }
289
290 assert_eq!(
291 s,
292 concat!(
293 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBB\r\n",
294 " CCCCCCCCCCCCC"
295 )
296 );
297 }
298
299 #[test]
300 fn optional_breakpoint() {
301 let mut s = "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
302 .to_owned();
303 let line_len = s.len();
304
305 {
306 let mut w = EmailWriter::new(&mut s, line_len, 0, true);
307 w.space();
308 w.folding().write_str("BBBBBBBBBB").unwrap();
309 w.space();
310 w.folding().write_str("CCCCCCCCCC").unwrap();
311 }
312
313 assert_eq!(
314 s,
315 concat!(
316 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n",
317 " BBBBBBBBBB CCCCCCCCCC",
318 )
319 );
320 }
321
322 #[test]
323 fn double_spaces_issue_949() {
324 let mut s = "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ".to_string();
325 let line_len = s.len();
326
327 {
328 let mut w = EmailWriter::new(&mut s, line_len, 0, true);
329 w.folding().write_str("BBBBBBBBBBBBB ").unwrap();
330 crate::headers::rfc2047::encode("sélection", &mut w).unwrap();
331 }
332
333 assert_eq!(
334 s,
335 concat!(
336 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBB\r\n",
337 " =?utf-8?b?c8OpbGVjdGlvbg==?=",
338 )
339 );
340 }
341
342 #[test]
343 fn double_spaces_issue_949_no_space() {
344 let mut s = "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ".to_string();
345 let line_len = s.len();
346
347 {
348 let mut w = EmailWriter::new(&mut s, line_len, 0, true);
349 w.folding().write_str("BBBBBBBBBBBBBBB").unwrap();
350 crate::headers::rfc2047::encode("sélection", &mut w).unwrap();
351 }
352
353 assert_eq!(
354 s,
355 concat!(
356 "Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBB=?utf-8?b?cw==?=\r\n",
357 " =?utf-8?b?w6lsZWN0aW9u?=",
358 )
359 );
360 }
361}