another_html_builder/
content.rs

1//! Attribute related module. This contains a wrapper to escape values.
2
3const CONTENT_ESCAPE: [char; 6] = ['&', '<', '>', '"', '\'', '/'];
4
5/// Wrapper around a [str] that will escape the content when writing.
6///
7/// This implementation will transform:
8/// - `&` to `&amp;`
9/// - `<` to `&lt;`
10/// - `>` to `&gt;`
11/// - `"` to `&quot;`
12/// - `'` to `&#x27;`
13/// - `/` to `&#x2F;`
14pub struct EscapedContent<'a>(pub &'a str);
15
16impl std::fmt::Display for EscapedContent<'_> {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        if self.0.is_empty() {
19            return Ok(());
20        }
21        let mut start: usize = 0;
22        while let Some(index) = self.0[start..].find(CONTENT_ESCAPE) {
23            if index > 0 {
24                f.write_str(&self.0[start..(start + index)])?;
25            }
26            let begin = start + index;
27            debug_assert!(start <= begin);
28            let end = begin + 1;
29            debug_assert!(begin < self.0.len());
30            debug_assert!(begin < end);
31            debug_assert!(end <= self.0.len());
32            match &self.0[begin..end] {
33                "&" => f.write_str("&amp;")?,
34                "<" => f.write_str("&lt;")?,
35                ">" => f.write_str("&gt;")?,
36                "\"" => f.write_str("&quot;")?,
37                "'" => f.write_str("&#x27;")?,
38                "/" => f.write_str("&#x2F;")?,
39                other => f.write_str(other)?,
40            };
41            start = end;
42            debug_assert!(start <= self.0.len());
43        }
44        f.write_str(&self.0[start..])?;
45        Ok(())
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    #[test_case::test_case("hello world", "hello world"; "without character to escape")]
52    #[test_case::test_case("a\"b", "a&quot;b"; "with special in the middle")]
53    #[test_case::test_case("\"a", "&quot;a"; "with special at the beginning")]
54    #[test_case::test_case("a\"", "a&quot;"; "with special at the end")]
55    fn escaping_content(input: &str, expected: &str) {
56        assert_eq!(format!("{}", super::EscapedContent(input)), expected);
57    }
58}