email_encoding/headers/
rfc2047.rs1use core::fmt::{self, Write};
6
7use super::{utils, writer::EmailWriter, MAX_LINE_LEN};
8
9const ENCODING_START_PREFIX: &str = "=?utf-8?b?";
10const ENCODING_END_SUFFIX: &str = "?=";
11
12pub fn encode(mut s: &str, w: &mut EmailWriter<'_>) -> fmt::Result {
31 let mut wrote = false;
32
33 while !s.is_empty() {
34 let remaining_line_len = MAX_LINE_LEN.saturating_sub(
35 ENCODING_START_PREFIX.len() + ENCODING_END_SUFFIX.len() + w.line_len() + "\r\n".len(),
36 );
37 let unencoded_remaining_line_len = remaining_line_len / 4 * 3;
38
39 let mut word =
40 utils::truncate_to_char_boundary(s, unencoded_remaining_line_len.min(s.len()));
41 if word.is_empty() {
42 if wrote || w.has_spaces() {
43 w.new_line()?;
45 if !w.has_spaces() {
46 w.space();
51 }
52 continue;
53 }
54
55 word = &s[..s.chars().next().expect("`s` is empty").len_utf8()];
58 }
59
60 w.write_str(ENCODING_START_PREFIX)?;
62
63 let encoder = base64::display::Base64Display::new(
65 word.as_bytes(),
66 &base64::engine::general_purpose::STANDARD,
67 );
68 write!(w, "{}", encoder)?;
69
70 w.write_str(ENCODING_END_SUFFIX)?;
72
73 s = &s[word.len()..];
74 wrote = true;
75 }
76
77 Ok(())
78}
79
80#[cfg(test)]
81mod tests {
82 use alloc::string::String;
83
84 use pretty_assertions::assert_eq;
85
86 use super::*;
87
88 #[test]
89 fn empty() {
90 let mut s = String::new();
91 let line_len = s.len();
92
93 {
94 let mut w = EmailWriter::new(&mut s, line_len, 0, false);
95 encode("", &mut w).unwrap();
96 }
97
98 assert_eq!(s, "");
99 }
100
101 #[test]
102 fn basic() {
103 let mut s = String::new();
104 let line_len = s.len();
105
106 {
107 let mut w = EmailWriter::new(&mut s, line_len, 0, false);
108 encode("abcd", &mut w).unwrap();
109 }
110
111 assert_eq!(s, "=?utf-8?b?YWJjZA==?=");
112 }
113
114 #[test]
115 fn basic_nopad() {
116 let mut s = String::new();
117 let line_len = s.len();
118
119 {
120 let mut w = EmailWriter::new(&mut s, line_len, 0, false);
121 encode("abcdef", &mut w).unwrap();
122 }
123
124 assert_eq!(s, "=?utf-8?b?YWJjZGVm?=");
125 }
126
127 #[test]
128 fn long() {
129 let mut s = String::new();
130 let line_len = s.len();
131
132 {
133 let mut w = EmailWriter::new(&mut s, line_len, 0, false);
134 encode(&"lettre".repeat(20), &mut w).unwrap();
135 }
136
137 assert_eq!(
138 s,
139 concat!(
140 "=?utf-8?b?bGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0?=\r\n",
141 " =?utf-8?b?dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJl?=\r\n",
142 " =?utf-8?b?bGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJlbGV0dHJl?="
143 )
144 );
145 }
146
147 #[test]
148 fn long_encoded() {
149 let mut s = String::new();
150 let line_len = s.len();
151
152 {
153 let mut w = EmailWriter::new(&mut s, line_len, 0, false);
154 encode(&"hétérogénéité".repeat(16), &mut w).unwrap();
155 }
156
157 assert_eq!(
158 s,
159 concat!(
160 "=?utf-8?b?aMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9n?=\r\n",
161 " =?utf-8?b?w6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOp?=\r\n",
162 " =?utf-8?b?aMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9n?=\r\n",
163 " =?utf-8?b?w6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOp?=\r\n",
164 " =?utf-8?b?aMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9n?=\r\n",
165 " =?utf-8?b?w6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOpaMOpdMOpcm9nw6luw6lpdMOp?=\r\n",
166 " =?utf-8?b?aMOpdMOpcm9nw6luw6lpdMOp?=",
167 )
168 );
169 }
170}