1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
pub trait ToHtml {
fn to_html(&self, out: &mut dyn Write) -> io::Result<()>;
}
#[allow(dead_code)]
pub struct Html<T>(pub T);
impl<T: Display> ToHtml for Html<T> {
#[inline]
fn to_html(&self, out: &mut dyn Write) -> io::Result<()> {
write!(out, "{}", self.0)
}
}
impl<T: Display> ToHtml for T {
#[inline]
fn to_html(&self, out: &mut dyn Write) -> io::Result<()> {
write!(ToHtmlEscapingWriter(out), "{}", self)
}
}
struct ToHtmlEscapingWriter<'a>(&'a mut dyn Write);
impl<'a> Write for ToHtmlEscapingWriter<'a> {
#[inline]
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
let n = data
.iter()
.take_while(|&&c| {
c != b'"' && c != b'&' && c != b'\'' && c != b'<' && c != b'>'
})
.count();
if n > 0 {
self.0.write(&data[0..n])
} else {
Self::write_one_byte_escaped(&mut self.0, data)
}
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}
impl<'a> ToHtmlEscapingWriter<'a> {
#[inline(never)]
fn write_one_byte_escaped(
out: &mut impl Write,
data: &[u8],
) -> io::Result<usize> {
let next = data.get(0);
out.write_all(match next {
Some(b'"') => b""",
Some(b'&') => b"&",
Some(b'<') => b"<",
Some(b'>') => b">",
None => return Ok(0),
_ => b"'",
})?;
Ok(1)
}
}