html_escape/encode/html_entity/mod.rs
1mod unquoted_attribute;
2
3use alloc::{borrow::Cow, string::String, vec::Vec};
4use core::str::from_utf8_unchecked;
5#[cfg(feature = "std")]
6use std::io::{self, Write};
7
8pub use unquoted_attribute::*;
9
10macro_rules! escape_impl {
11 (@inner [$dollar:tt] $name:ident; $($l:expr => $r:expr),+ $(,)*) => {
12 macro_rules! $name {
13 ($dollar e:expr) => {
14 match $dollar e {
15 $($l => break $r,)+
16 _ => (),
17 }
18 };
19 (vec $dollar e:expr, $dollar v:ident, $dollar b:ident, $dollar start:ident, $dollar end:ident) => {
20 match $dollar e {
21 $($l => {
22 $dollar v.extend_from_slice(&$dollar b[$dollar start..$dollar end]);
23 $dollar start = $dollar end + 1;
24 $dollar v.extend_from_slice($r);
25 })+
26 _ => (),
27 }
28
29 $dollar end += 1;
30 };
31 (writer $dollar e:expr, $dollar w:ident, $dollar b:ident, $dollar start:ident, $dollar end:ident) => {
32 match $dollar e {
33 $($l => {
34 $dollar w.write_all(&$dollar b[$dollar start..$dollar end])?;
35 $dollar start = $dollar end + 1;
36 $dollar w.write_all($r)?;
37 })+
38 _ => (),
39 }
40
41 $dollar end += 1;
42 };
43 }
44 };
45 ($name:ident; $($l:expr => $r:expr),+ $(,)*) => {
46 escape_impl! {
47 @inner [$]
48 $name;
49 $($l => $r.as_ref(),)*
50 }
51 };
52}
53
54escape_impl! {
55 escape_text_minimal;
56 b'&' => b"&",
57 b'<' => b"<",
58}
59
60escape_impl! {
61 escape_text;
62 b'&' => b"&",
63 b'<' => b"<",
64 b'>' => b">",
65}
66
67escape_impl! {
68 escape_double_quote;
69 b'&' => b"&",
70 b'<' => b"<",
71 b'>' => b">",
72 b'"' => b""",
73}
74
75escape_impl! {
76 escape_single_quote;
77 b'&' => b"&",
78 b'<' => b"<",
79 b'>' => b">",
80 b'\'' => b"'",
81}
82
83escape_impl! {
84 escape_quote;
85 b'&' => b"&",
86 b'<' => b"<",
87 b'>' => b">",
88 b'"' => b""",
89 b'\'' => b"'",
90}
91
92escape_impl! {
93 escape_safe;
94 b'&' => b"&",
95 b'<' => b"<",
96 b'>' => b">",
97 b'"' => b""",
98 b'\'' => b"'",
99 b'/' => b"/",
100}
101
102macro_rules! encode_impl {
103 ($(#[$attr: meta])* $escape_macro:ident; $(#[$encode_attr: meta])* $encode_name: ident; $(#[$encode_to_string_attr: meta])* $encode_to_string_name: ident; $(#[$encode_to_vec_attr: meta])* $encode_to_vec_name: ident; $(#[$encode_to_writer_attr: meta])* $encode_to_writer_name: ident $(;)*) => {
104 $(#[$encode_attr])*
105 ///
106 $(#[$attr])*
107 #[inline]
108 pub fn $encode_name<S: ?Sized + AsRef<str>>(text: &S) -> Cow<'_, str> {
109 let text = text.as_ref();
110 let text_bytes = text.as_bytes();
111 let text_length = text_bytes.len();
112
113 let mut p = 0;
114 let mut e;
115
116 let first = loop {
117 if p == text_length {
118 return Cow::from(text);
119 }
120
121 e = text_bytes[p];
122
123 $escape_macro!(e);
124
125 p += 1;
126 };
127
128 let mut v = Vec::with_capacity(text_length + 5);
129
130 v.extend_from_slice(&text_bytes[..p]);
131 v.extend_from_slice(first);
132
133 // SAFETY: `text_bytes[p]` is the ASCII byte which has just been escaped, so the rest is valid UTF-8.
134 $encode_to_vec_name(unsafe { from_utf8_unchecked(&text_bytes[(p + 1)..]) }, &mut v);
135
136 // SAFETY: `v` only contains slices of `text`, split at ASCII bytes, plus ASCII escape sequences, so it is valid UTF-8.
137 Cow::from(unsafe { String::from_utf8_unchecked(v) })
138 }
139
140 $(#[$encode_to_string_attr])*
141 ///
142 $(#[$attr])*
143 #[inline]
144 pub fn $encode_to_string_name<S: AsRef<str>>(text: S, output: &mut String) -> &str {
145 // SAFETY: the encoded data is valid UTF-8, so `output` remains a valid `String`.
146 unsafe { from_utf8_unchecked($encode_to_vec_name(text, output.as_mut_vec())) }
147 }
148
149 $(#[$encode_to_vec_attr])*
150 ///
151 $(#[$attr])*
152 #[inline]
153 pub fn $encode_to_vec_name<S: AsRef<str>>(text: S, output: &mut Vec<u8>) -> &[u8] {
154 let text = text.as_ref();
155 let text_bytes = text.as_bytes();
156 let text_length = text_bytes.len();
157
158 output.reserve(text_length);
159
160 let current_length = output.len();
161
162 let mut start = 0;
163 let mut end = 0;
164
165 for e in text_bytes.iter().copied() {
166 $escape_macro!(vec e, output, text_bytes, start, end);
167 }
168
169 output.extend_from_slice(&text_bytes[start..end]);
170
171 &output[current_length..]
172 }
173
174 #[cfg(feature = "std")]
175 $(#[$encode_to_writer_attr])*
176 ///
177 $(#[$attr])*
178 #[inline]
179 pub fn $encode_to_writer_name<S: AsRef<str>, W: Write>(text: S, output: &mut W) -> Result<(), io::Error> {
180 let text = text.as_ref();
181 let text_bytes = text.as_bytes();
182
183 let mut start = 0;
184 let mut end = 0;
185
186 for e in text_bytes.iter().copied() {
187 $escape_macro!(writer e, output, text_bytes, start, end);
188 }
189
190 output.write_all(&text_bytes[start..end])
191 }
192 };
193}
194
195encode_impl! {
196 /// The following characters are escaped:
197 ///
198 /// * `&` => `&`
199 /// * `<` => `<`
200 escape_text_minimal;
201 /// Encode text used as regular HTML text.
202 encode_text_minimal;
203 /// Write text used as regular HTML text to a mutable `String` reference and return the encoded string slice.
204 encode_text_minimal_to_string;
205 /// Write text used as regular HTML text to a mutable `Vec<u8>` reference and return the encoded data slice.
206 encode_text_minimal_to_vec;
207 /// Write text used as regular HTML text to a writer.
208 encode_text_minimal_to_writer;
209}
210
211encode_impl! {
212 /// The following characters are escaped:
213 ///
214 /// * `&` => `&`
215 /// * `<` => `<`
216 /// * `>` => `>`
217 escape_text;
218 /// Encode text used as regular HTML text.
219 encode_text;
220 /// Write text used as regular HTML text to a mutable `String` reference and return the encoded string slice.
221 encode_text_to_string;
222 /// Write text used as regular HTML text to a mutable `Vec<u8>` reference and return the encoded data slice.
223 encode_text_to_vec;
224 /// Write text used as regular HTML text to a writer.
225 encode_text_to_writer;
226}
227
228encode_impl! {
229 /// The following characters are escaped:
230 ///
231 /// * `&` => `&`
232 /// * `<` => `<`
233 /// * `>` => `>`
234 /// * `"` => `"`
235 escape_double_quote;
236 /// Encode text used in a double-quoted attribute.
237 encode_double_quoted_attribute;
238 /// Write text used in a double-quoted attribute to a mutable `String` reference and return the encoded string slice.
239 encode_double_quoted_attribute_to_string;
240 /// Write text used in a double-quoted attribute to a mutable `Vec<u8>` reference and return the encoded data slice.
241 encode_double_quoted_attribute_to_vec;
242 /// Write text used in a double-quoted attribute to a writer.
243 encode_double_quoted_attribute_to_writer;
244}
245
246encode_impl! {
247 /// The following characters are escaped:
248 ///
249 /// * `&` => `&`
250 /// * `<` => `<`
251 /// * `>` => `>`
252 /// * `'` => `'`
253 escape_single_quote;
254 /// Encode text used in a single-quoted attribute.
255 encode_single_quoted_attribute;
256 /// Write text used in a single-quoted attribute to a mutable `String` reference and return the encoded string slice.
257 encode_single_quoted_attribute_to_string;
258 /// Write text used in a single-quoted attribute to a mutable `Vec<u8>` reference and return the encoded data slice.
259 encode_single_quoted_attribute_to_vec;
260 /// Write text used in a single-quoted attribute to a writer.
261 encode_single_quoted_attribute_to_writer;
262}
263
264encode_impl! {
265 /// The following characters (HTML reserved characters) are escaped:
266 ///
267 /// * `&` => `&`
268 /// * `<` => `<`
269 /// * `>` => `>`
270 /// * `"` => `"`
271 /// * `'` => `'`
272 escape_quote;
273 /// Encode text used in a quoted attribute.
274 encode_quoted_attribute;
275 /// Write text used in a quoted attribute to a mutable `String` reference and return the encoded string slice.
276 encode_quoted_attribute_to_string;
277 /// Write text used in a quoted attribute to a mutable `Vec<u8>` reference and return the encoded data slice.
278 encode_quoted_attribute_to_vec;
279 /// Write text used in a quoted attribute to a writer.
280 encode_quoted_attribute_to_writer;
281}
282
283encode_impl! {
284 /// The following characters are escaped:
285 ///
286 /// * `&` => `&`
287 /// * `<` => `<`
288 /// * `>` => `>`
289 /// * `"` => `"`
290 /// * `'` => `'`
291 /// * `/` => `/`
292 escape_safe;
293 /// Encode `&`, `<`, `>`, `"`, `'`, and `/` as HTML entities.
294 ///
295 /// This function is not a context-independent sanitizer.
296 /// Use a context-specific encoder for text, attributes, scripts, and styles.
297 encode_safe;
298 /// Encode `&`, `<`, `>`, `"`, `'`, and `/` as HTML entities, write the result to a mutable `String` reference, and return the encoded string slice.
299 ///
300 /// This function is not a context-independent sanitizer.
301 /// Use a context-specific encoder for text, attributes, scripts, and styles.
302 encode_safe_to_string;
303 /// Encode `&`, `<`, `>`, `"`, `'`, and `/` as HTML entities, write the result to a mutable `Vec<u8>` reference, and return the encoded data slice.
304 ///
305 /// This function is not a context-independent sanitizer.
306 /// Use a context-specific encoder for text, attributes, scripts, and styles.
307 encode_safe_to_vec;
308 /// Encode `&`, `<`, `>`, `"`, `'`, and `/` as HTML entities and write the result to a writer.
309 ///
310 /// This function is not a context-independent sanitizer.
311 /// Use a context-specific encoder for text, attributes, scripts, and styles.
312 encode_safe_to_writer;
313}