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
pub trait ToHtml {
#[inline]
fn to_html(&self, out: &mut 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 Write) -> io::Result<()> {
write!(out, "{}", self.0)
}
}
impl<T: Display> ToHtml for T {
#[inline]
fn to_html(&self, out: &mut Write) -> io::Result<()> {
write!(ToHtmlEscapingWriter(out), "{}", self)
}
}
struct ToHtmlEscapingWriter<'a>(&'a mut Write);
impl<'a> Write for ToHtmlEscapingWriter<'a> {
#[inline]
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
let n = data.into_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 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)
}
}