html_escape/encode/html_entity/
mod.rs

1mod 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"&lt;",
62}
63
64escape_impl! {
65    escape_text;
66    b'&' => b"&amp;",
67    b'<' => b"&lt;",
68    b'>' => b"&gt;",
69}
70
71escape_impl! {
72    escape_double_quote;
73    b'&' => b"&amp;",
74    b'<' => b"&lt;",
75    b'>' => b"&gt;",
76    b'"' => b"&quot;",
77}
78
79escape_impl! {
80    escape_single_quote;
81    b'&' => b"&amp;",
82    b'<' => b"&lt;",
83    b'>' => b"&gt;",
84    b'\'' => b"&#x27;",
85}
86
87escape_impl! {
88    escape_quote;
89    b'&' => b"&amp;",
90    b'<' => b"&lt;",
91    b'>' => b"&gt;",
92    b'"' => b"&quot;",
93    b'\'' => b"&#x27;",
94}
95
96escape_impl! {
97    escape_safe;
98    b'&' => b"&amp;",
99    b'<' => b"&lt;",
100    b'>' => b"&gt;",
101    b'"' => b"&quot;",
102    b'\'' => b"&#x27;",
103    b'/' => b"&#x2F;",
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        ///
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
196encode_impl! {
197    /// The following characters are escaped:
198    ///
199    /// * `&` => `&amp;`
200    /// * `<` => `&lt;`
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
212encode_impl! {
213    /// The following characters are escaped:
214    ///
215    /// * `&` => `&amp;`
216    /// * `<` => `&lt;`
217    /// * `>` => `&gt;`
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
229encode_impl! {
230    /// The following characters are escaped:
231    ///
232    /// * `&` => `&amp;`
233    /// * `<` => `&lt;`
234    /// * `>` => `&gt;`
235    /// * `"` => `&quot;`
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
247encode_impl! {
248    /// The following characters are escaped:
249    ///
250    /// * `&` => `&amp;`
251    /// * `<` => `&lt;`
252    /// * `>` => `&gt;`
253    /// * `'` => `&#x27;`
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
265encode_impl! {
266    /// The following characters (HTML reserved characters)  are escaped:
267    ///
268    /// * `&` => `&amp;`
269    /// * `<` => `&lt;`
270    /// * `>` => `&gt;`
271    /// * `"` => `&quot;`
272    /// * `'` => `&#x27;`
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
284encode_impl! {
285    /// The following characters are escaped:
286    ///
287    /// * `&` => `&amp;`
288    /// * `<` => `&lt;`
289    /// * `>` => `&gt;`
290    /// * `"` => `&quot;`
291    /// * `'` => `&#x27;`
292    /// * `/` => `&#x2F;`
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}