1use std::borrow::Cow;
4
5enum Value {
6 Char(char),
7 Str(&'static str)
8}
9
10impl Value {
11 fn dispatch_for_attribute(c: char) -> Value {
12 match c {
13 '<' => Value::Str("<"),
14 '>' => Value::Str(">"),
15 '"' => Value::Str("""),
16 '\'' => Value::Str("'"),
17 '&' => Value::Str("&"),
18 '\n' => Value::Str("
"),
19 '\r' => Value::Str("
"),
20 _ => Value::Char(c)
21 }
22 }
23
24 fn dispatch_for_pcdata(c: char) -> Value {
25 match c {
26 '<' => Value::Str("<"),
27 '&' => Value::Str("&"),
28 _ => Value::Char(c)
29 }
30 }
31}
32
33enum Process<'a> {
34 Borrowed(&'a str),
35 Owned(String)
36}
37
38impl<'a> Process<'a> {
39 fn process(&mut self, (i, next): (usize, Value)) {
40 match next {
41 Value::Str(s) => match *self {
42 Process::Owned(ref mut o) => o.push_str(s),
43 Process::Borrowed(b) => {
44 let mut r = String::with_capacity(b.len() + s.len());
45 r.push_str(&b[..i]);
46 r.push_str(s);
47 *self = Process::Owned(r);
48 }
49 },
50 Value::Char(c) => match *self {
51 Process::Borrowed(_) => {}
52 Process::Owned(ref mut o) => o.push(c)
53 }
54 }
55 }
56
57 fn into_result(self) -> Cow<'a, str> {
58 match self {
59 Process::Borrowed(b) => Cow::Borrowed(b),
60 Process::Owned(o) => Cow::Owned(o)
61 }
62 }
63}
64
65impl<'a> Extend<(usize, Value)> for Process<'a> {
66 fn extend<I: IntoIterator<Item=(usize, Value)>>(&mut self, it: I) {
67 for v in it.into_iter() {
68 self.process(v);
69 }
70 }
71}
72
73fn escape_str(s: &str, dispatch: fn(char) -> Value) -> Cow<str> {
74 let mut p = Process::Borrowed(s);
75 p.extend(s.char_indices().map(|(ind, c)| (ind, dispatch(c))));
76 p.into_result()
77}
78
79#[inline]
94pub fn escape_str_attribute(s: &str) -> Cow<str> {
95 escape_str(s, Value::dispatch_for_attribute)
96}
97
98#[inline]
110pub fn escape_str_pcdata(s: &str) -> Cow<str> {
111 escape_str(s, Value::dispatch_for_pcdata)
112}
113
114#[cfg(test)]
115mod tests {
116 use super::{escape_str_pcdata, escape_str_attribute};
117
118 #[test]
121 fn test_escape_multibyte_code_points() {
122 assert_eq!(escape_str_attribute("☃<"), "☃<");
123 assert_eq!(escape_str_pcdata("☃<"), "☃<");
124 }
125}
126