Skip to main content

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