1pub fn escape_html(input: &str) -> String {
17 if !input
20 .as_bytes()
21 .iter()
22 .any(|&b| matches!(b, b'&' | b'<' | b'>' | b'"' | b'\''))
23 {
24 return input.to_string();
25 }
26 let mut out = String::with_capacity(input.len());
27 for c in input.chars() {
28 match c {
29 '&' => out.push_str("&"),
30 '<' => out.push_str("<"),
31 '>' => out.push_str(">"),
32 '"' => out.push_str("""),
33 '\'' => out.push_str("'"),
34 _ => out.push(c),
35 }
36 }
37 out
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43
44 #[test]
45 fn escapes_all_five_special_chars() {
46 assert_eq!(escape_html("&<>\"'"), "&<>"'");
47 }
48
49 #[test]
50 fn escapes_ampersand_first_to_avoid_double_escape() {
51 assert_eq!(escape_html("<&>"), "<&>");
53 }
54
55 #[test]
56 fn leaves_plain_text_untouched() {
57 assert_eq!(escape_html("hello world"), "hello world");
58 }
59
60 #[test]
61 fn empty_input_returns_empty() {
62 assert_eq!(escape_html(""), "");
63 }
64}