1 | mod unquoted_attribute; |
2 | |
3 | use core::str::from_utf8_unchecked; |
4 | |
5 | use alloc::borrow::Cow; |
6 | use alloc::string::String; |
7 | use alloc::vec::Vec; |
8 | |
9 | #[cfg (feature = "std" )] |
10 | use std::io::{self, Write}; |
11 | |
12 | pub use unquoted_attribute::*; |
13 | |
14 | macro_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 | |
58 | escape_impl! { |
59 | escape_text_minimal; |
60 | b'&' => b"&" , |
61 | b'<' => b"<" , |
62 | } |
63 | |
64 | escape_impl! { |
65 | escape_text; |
66 | b'&' => b"&" , |
67 | b'<' => b"<" , |
68 | b'>' => b">" , |
69 | } |
70 | |
71 | escape_impl! { |
72 | escape_double_quote; |
73 | b'&' => b"&" , |
74 | b'<' => b"<" , |
75 | b'>' => b">" , |
76 | b'"' => b""" , |
77 | } |
78 | |
79 | escape_impl! { |
80 | escape_single_quote; |
81 | b'&' => b"&" , |
82 | b'<' => b"<" , |
83 | b'>' => b">" , |
84 | b' \'' => b"'" , |
85 | } |
86 | |
87 | escape_impl! { |
88 | escape_quote; |
89 | b'&' => b"&" , |
90 | b'<' => b"<" , |
91 | b'>' => b">" , |
92 | b'"' => b""" , |
93 | b' \'' => b"'" , |
94 | } |
95 | |
96 | escape_impl! { |
97 | escape_safe; |
98 | b'&' => b"&" , |
99 | b'<' => b"<" , |
100 | b'>' => b">" , |
101 | b'"' => b""" , |
102 | b' \'' => b"'" , |
103 | b'/' => b"/" , |
104 | } |
105 | |
106 | macro_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 | /// |
110 | $(#[$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 | /// |
144 | $(#[$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 | /// |
152 | $(#[$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 | /// |
178 | $(#[$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 | |
196 | encode_impl! { |
197 | /// The following characters are escaped: |
198 | /// |
199 | /// * `&` => `&` |
200 | /// * `<` => `<` |
201 | escape_text_minimal; |
202 | /// Encode text used as regular HTML text. |
203 | encode_text_minimal; |
204 | /// Write text used as regular HTML text to a mutable `String` reference and return the encoded string slice. |
205 | encode_text_minimal_to_string; |
206 | /// Write text used as regular HTML text to a mutable `Vec<u8>` reference and return the encoded data slice. |
207 | encode_text_minimal_to_vec; |
208 | /// Write text used as regular HTML text to a writer. |
209 | encode_text_minimal_to_writer; |
210 | } |
211 | |
212 | encode_impl! { |
213 | /// The following characters are escaped: |
214 | /// |
215 | /// * `&` => `&` |
216 | /// * `<` => `<` |
217 | /// * `>` => `>` |
218 | escape_text; |
219 | /// Encode text used as regular HTML text. |
220 | encode_text; |
221 | /// Write text used as regular HTML text to a mutable `String` reference and return the encoded string slice. |
222 | encode_text_to_string; |
223 | /// Write text used as regular HTML text to a mutable `Vec<u8>` reference and return the encoded data slice. |
224 | encode_text_to_vec; |
225 | /// Write text used as regular HTML text to a writer. |
226 | encode_text_to_writer; |
227 | } |
228 | |
229 | encode_impl! { |
230 | /// The following characters are escaped: |
231 | /// |
232 | /// * `&` => `&` |
233 | /// * `<` => `<` |
234 | /// * `>` => `>` |
235 | /// * `"` => `"` |
236 | escape_double_quote; |
237 | /// Encode text used in a double-quoted attribute. |
238 | encode_double_quoted_attribute; |
239 | /// Write text used in a double-quoted attribute to a mutable `String` reference and return the encoded string slice. |
240 | encode_double_quoted_attribute_to_string; |
241 | /// Write text used in a double-quoted attribute to a mutable `Vec<u8>` reference and return the encoded data slice. |
242 | encode_double_quoted_attribute_to_vec; |
243 | /// Write text used in a double-quoted attribute to a writer. |
244 | encode_double_quoted_attribute_to_writer; |
245 | } |
246 | |
247 | encode_impl! { |
248 | /// The following characters are escaped: |
249 | /// |
250 | /// * `&` => `&` |
251 | /// * `<` => `<` |
252 | /// * `>` => `>` |
253 | /// * `'` => `'` |
254 | escape_single_quote; |
255 | /// Encode text used in a single-quoted attribute. |
256 | encode_single_quoted_attribute; |
257 | /// Write text used in a single-quoted attribute to a mutable `String` reference and return the encoded string slice. |
258 | encode_single_quoted_attribute_to_string; |
259 | /// Write text used in a single-quoted attribute to a mutable `Vec<u8>` reference and return the encoded data slice. |
260 | encode_single_quoted_attribute_to_vec; |
261 | /// Write text used in a single-quoted attribute to a writer. |
262 | encode_single_quoted_attribute_to_writer; |
263 | } |
264 | |
265 | encode_impl! { |
266 | /// The following characters (HTML reserved characters) are escaped: |
267 | /// |
268 | /// * `&` => `&` |
269 | /// * `<` => `<` |
270 | /// * `>` => `>` |
271 | /// * `"` => `"` |
272 | /// * `'` => `'` |
273 | escape_quote; |
274 | /// Encode text used in a quoted attribute. |
275 | encode_quoted_attribute; |
276 | /// Write text used in a quoted attribute to a mutable `String` reference and return the encoded string slice. |
277 | encode_quoted_attribute_to_string; |
278 | /// Write text used in a quoted attribute to a mutable `Vec<u8>` reference and return the encoded data slice. |
279 | encode_quoted_attribute_to_vec; |
280 | /// Write text used in a quoted attribute to a writer. |
281 | encode_quoted_attribute_to_writer; |
282 | } |
283 | |
284 | encode_impl! { |
285 | /// The following characters are escaped: |
286 | /// |
287 | /// * `&` => `&` |
288 | /// * `<` => `<` |
289 | /// * `>` => `>` |
290 | /// * `"` => `"` |
291 | /// * `'` => `'` |
292 | /// * `/` => `/` |
293 | escape_safe; |
294 | /// Encode text to prevent special characters functioning. |
295 | encode_safe; |
296 | /// Encode text to prevent special characters functioning and write it to a mutable `String` reference and return the encoded string slice. |
297 | encode_safe_to_string; |
298 | /// Encode text to prevent special characters functioning and write it to a mutable `Vec<u8>` reference and return the encoded data slice. |
299 | encode_safe_to_vec; |
300 | /// Encode text to prevent special characters functioning and write it to a writer. |
301 | encode_safe_to_writer; |
302 | } |
303 | |