html_escape/encode/html_entity/
mod.rs1mod 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 $(#[$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 $encode_to_vec_name(unsafe { from_utf8_unchecked(&text_bytes[(p + 1)..]) }, &mut v);
134
135 Cow::from(unsafe { String::from_utf8_unchecked(v) })
136 }
137
138 $(#[$encode_to_string_attr])*
139 $(#[$attr])*
141 #[inline]
142 pub fn $encode_to_string_name<S: AsRef<str>>(text: S, output: &mut String) -> &str {
143 unsafe { from_utf8_unchecked($encode_to_vec_name(text, output.as_mut_vec())) }
144 }
145
146 $(#[$encode_to_vec_attr])*
147 $(#[$attr])*
149 #[inline]
150 pub fn $encode_to_vec_name<S: AsRef<str>>(text: S, output: &mut Vec<u8>) -> &[u8] {
151 let text = text.as_ref();
152 let text_bytes = text.as_bytes();
153 let text_length = text_bytes.len();
154
155 output.reserve(text_length);
156
157 let current_length = output.len();
158
159 let mut start = 0;
160 let mut end = 0;
161
162 for e in text_bytes.iter().copied() {
163 $escape_macro!(vec e, output, text_bytes, start, end);
164 }
165
166 output.extend_from_slice(&text_bytes[start..end]);
167
168 &output[current_length..]
169 }
170
171 #[cfg(feature = "std")]
172 $(#[$encode_to_writer_attr])*
173 $(#[$attr])*
175 #[inline]
176 pub fn $encode_to_writer_name<S: AsRef<str>, W: Write>(text: S, output: &mut W) -> Result<(), io::Error> {
177 let text = text.as_ref();
178 let text_bytes = text.as_bytes();
179
180 let mut start = 0;
181 let mut end = 0;
182
183 for e in text_bytes.iter().copied() {
184 $escape_macro!(writer e, output, text_bytes, start, end);
185 }
186
187 output.write_all(&text_bytes[start..end])
188 }
189 };
190}
191
192encode_impl! {
193 escape_text_minimal;
198 encode_text_minimal;
200 encode_text_minimal_to_string;
202 encode_text_minimal_to_vec;
204 encode_text_minimal_to_writer;
206}
207
208encode_impl! {
209 escape_text;
215 encode_text;
217 encode_text_to_string;
219 encode_text_to_vec;
221 encode_text_to_writer;
223}
224
225encode_impl! {
226 escape_double_quote;
233 encode_double_quoted_attribute;
235 encode_double_quoted_attribute_to_string;
237 encode_double_quoted_attribute_to_vec;
239 encode_double_quoted_attribute_to_writer;
241}
242
243encode_impl! {
244 escape_single_quote;
251 encode_single_quoted_attribute;
253 encode_single_quoted_attribute_to_string;
255 encode_single_quoted_attribute_to_vec;
257 encode_single_quoted_attribute_to_writer;
259}
260
261encode_impl! {
262 escape_quote;
270 encode_quoted_attribute;
272 encode_quoted_attribute_to_string;
274 encode_quoted_attribute_to_vec;
276 encode_quoted_attribute_to_writer;
278}
279
280encode_impl! {
281 escape_safe;
290 encode_safe;
292 encode_safe_to_string;
294 encode_safe_to_vec;
296 encode_safe_to_writer;
298}