html_escape/encode/html_entity/
mod.rs1mod unquoted_attribute;
2
3use core::str::from_utf8_unchecked;
4
5use alloc::borrow::Cow;
6use alloc::string::String;
7use alloc::vec::Vec;
8
9#[cfg(feature = "std")]
10use std::io::{self, Write};
11
12pub use unquoted_attribute::*;
13
14macro_rules! escape_impl {
15 (@inner [$dollar:tt] $name:ident; $($l:expr => $r:expr),+ $(,)*) => {
16 macro_rules! $name {
17 ($dollar e:expr) => {
18 match $dollar e {
19 $($l => break $r,)+
20 _ => (),
21 }
22 };
23 (vec $dollar e:expr, $dollar v:ident, $dollar b:ident, $dollar start:ident, $dollar end:ident) => {
24 match $dollar e {
25 $($l => {
26 $dollar v.extend_from_slice(&$dollar b[$dollar start..$dollar end]);
27 $dollar start = $dollar end + 1;
28 $dollar v.extend_from_slice($r);
29 })+
30 _ => (),
31 }
32
33 $dollar end += 1;
34 };
35 (writer $dollar e:expr, $dollar w:ident, $dollar b:ident, $dollar start:ident, $dollar end:ident) => {
36 match $dollar e {
37 $($l => {
38 $dollar w.write_all(&$dollar b[$dollar start..$dollar end])?;
39 $dollar start = $dollar end + 1;
40 $dollar w.write_all($r)?;
41 })+
42 _ => (),
43 }
44
45 $dollar end += 1;
46 };
47 }
48 };
49 ($name:ident; $($l:expr => $r:expr),+ $(,)*) => {
50 escape_impl! {
51 @inner [$]
52 $name;
53 $($l => $r.as_ref(),)*
54 }
55 };
56}
57
58escape_impl! {
59 escape_text_minimal;
60 b'&' => b"&",
61 b'<' => b"<",
62}
63
64escape_impl! {
65 escape_text;
66 b'&' => b"&",
67 b'<' => b"<",
68 b'>' => b">",
69}
70
71escape_impl! {
72 escape_double_quote;
73 b'&' => b"&",
74 b'<' => b"<",
75 b'>' => b">",
76 b'"' => b""",
77}
78
79escape_impl! {
80 escape_single_quote;
81 b'&' => b"&",
82 b'<' => b"<",
83 b'>' => b">",
84 b'\'' => b"'",
85}
86
87escape_impl! {
88 escape_quote;
89 b'&' => b"&",
90 b'<' => b"<",
91 b'>' => b">",
92 b'"' => b""",
93 b'\'' => b"'",
94}
95
96escape_impl! {
97 escape_safe;
98 b'&' => b"&",
99 b'<' => b"<",
100 b'>' => b">",
101 b'"' => b""",
102 b'\'' => b"'",
103 b'/' => b"/",
104}
105
106macro_rules! encode_impl {
107 ($(#[$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 $(;)*) => {
108 $(#[$encode_attr])*
109 $(#[$attr])*
111 #[inline]
112 pub fn $encode_name<S: ?Sized + AsRef<str>>(text: &S) -> Cow<str> {
113 let text = text.as_ref();
114 let text_bytes = text.as_bytes();
115 let text_length = text_bytes.len();
116
117 let mut p = 0;
118 let mut e;
119
120 let first = loop {
121 if p == text_length {
122 return Cow::from(text);
123 }
124
125 e = text_bytes[p];
126
127 $escape_macro!(e);
128
129 p += 1;
130 };
131
132 let mut v = Vec::with_capacity(text_length + 5);
133
134 v.extend_from_slice(&text_bytes[..p]);
135 v.extend_from_slice(first);
136
137 $encode_to_vec_name(unsafe { from_utf8_unchecked(&text_bytes[(p + 1)..]) }, &mut v);
138
139 Cow::from(unsafe { String::from_utf8_unchecked(v) })
140 }
141
142 $(#[$encode_to_string_attr])*
143 $(#[$attr])*
145 #[inline]
146 pub fn $encode_to_string_name<S: AsRef<str>>(text: S, output: &mut String) -> &str {
147 unsafe { from_utf8_unchecked($encode_to_vec_name(text, output.as_mut_vec())) }
148 }
149
150 $(#[$encode_to_vec_attr])*
151 $(#[$attr])*
153 #[inline]
154 pub fn $encode_to_vec_name<S: AsRef<str>>(text: S, output: &mut Vec<u8>) -> &[u8] {
155 let text = text.as_ref();
156 let text_bytes = text.as_bytes();
157 let text_length = text_bytes.len();
158
159 output.reserve(text_length);
160
161 let current_length = output.len();
162
163 let mut start = 0;
164 let mut end = 0;
165
166 for e in text_bytes.iter().copied() {
167 $escape_macro!(vec e, output, text_bytes, start, end);
168 }
169
170 output.extend_from_slice(&text_bytes[start..end]);
171
172 &output[current_length..]
173 }
174
175 #[cfg(feature = "std")]
176 $(#[$encode_to_writer_attr])*
177 $(#[$attr])*
179 #[inline]
180 pub fn $encode_to_writer_name<S: AsRef<str>, W: Write>(text: S, output: &mut W) -> Result<(), io::Error> {
181 let text = text.as_ref();
182 let text_bytes = text.as_bytes();
183
184 let mut start = 0;
185 let mut end = 0;
186
187 for e in text_bytes.iter().copied() {
188 $escape_macro!(writer e, output, text_bytes, start, end);
189 }
190
191 output.write_all(&text_bytes[start..end])
192 }
193 };
194}
195
196encode_impl! {
197 escape_text_minimal;
202 encode_text_minimal;
204 encode_text_minimal_to_string;
206 encode_text_minimal_to_vec;
208 encode_text_minimal_to_writer;
210}
211
212encode_impl! {
213 escape_text;
219 encode_text;
221 encode_text_to_string;
223 encode_text_to_vec;
225 encode_text_to_writer;
227}
228
229encode_impl! {
230 escape_double_quote;
237 encode_double_quoted_attribute;
239 encode_double_quoted_attribute_to_string;
241 encode_double_quoted_attribute_to_vec;
243 encode_double_quoted_attribute_to_writer;
245}
246
247encode_impl! {
248 escape_single_quote;
255 encode_single_quoted_attribute;
257 encode_single_quoted_attribute_to_string;
259 encode_single_quoted_attribute_to_vec;
261 encode_single_quoted_attribute_to_writer;
263}
264
265encode_impl! {
266 escape_quote;
274 encode_quoted_attribute;
276 encode_quoted_attribute_to_string;
278 encode_quoted_attribute_to_vec;
280 encode_quoted_attribute_to_writer;
282}
283
284encode_impl! {
285 escape_safe;
294 encode_safe;
296 encode_safe_to_string;
298 encode_safe_to_vec;
300 encode_safe_to_writer;
302}