1use std::collections::VecDeque;
2
3use std::char;
4
5macro_rules! try_option {
6 ($o:expr) => {
7 match $o {
8 Some(s) => s,
9 None => return None,
10 }
11 }
12}
13
14pub fn unescape(s: &str) -> Option<String> {
17 let mut queue : VecDeque<_> = String::from(s).chars().collect();
18 let mut s = String::new();
19
20 while let Some(c) = queue.pop_front() {
21 if c != '\\' {
22 s.push(c);
23 continue;
24 }
25
26 match queue.pop_front() {
27 Some('b') => s.push('\u{0008}'),
28 Some('f') => s.push('\u{000C}'),
29 Some('n') => s.push('\n'),
30 Some('r') => s.push('\r'),
31 Some('t') => s.push('\t'),
32 Some('\'') => s.push('\''),
33 Some('\"') => s.push('\"'),
34 Some('\\') => s.push('\\'),
35 Some('u') => s.push(try_option!(unescape_unicode(&mut queue))),
36 Some('x') => s.push(try_option!(unescape_byte(&mut queue))),
37 Some(c) if c.is_digit(8) => s.push(try_option!(unescape_octal(c, &mut queue))),
38 _ => return None
39 };
40 }
41
42 Some(s)
43}
44
45fn unescape_unicode(queue: &mut VecDeque<char>) -> Option<char> {
46 let mut s = String::new();
47
48 for _ in 0..4 {
49 s.push(try_option!(queue.pop_front()));
50 }
51
52 let u = try_option!(u32::from_str_radix(&s, 16).ok());
53 char::from_u32(u)
54}
55
56fn unescape_byte(queue: &mut VecDeque<char>) -> Option<char> {
57 let mut s = String::new();
58
59 for _ in 0..2 {
60 s.push(try_option!(queue.pop_front()));
61 }
62
63 let u = try_option!(u32::from_str_radix(&s, 16).ok());
64 char::from_u32(u)
65}
66
67fn unescape_octal(c: char, queue: &mut VecDeque<char>) -> Option<char> {
68 match unescape_octal_leading(c, queue) {
69 Some(ch) => {
70 let _ = queue.pop_front();
71 let _ = queue.pop_front();
72 Some(ch)
73 }
74 None => unescape_octal_no_leading(c, queue)
75 }
76}
77
78fn unescape_octal_leading(c: char, queue: &VecDeque<char>) -> Option<char> {
79 if c != '0' && c != '1' && c != '2' && c != '3' {
80 return None;
81 }
82
83 let mut s = String::new();
84 s.push(c);
85 s.push(*try_option!(queue.get(0)));
86 s.push(*try_option!(queue.get(1)));
87
88 let u = try_option!(u32::from_str_radix(&s, 8).ok());
89 char::from_u32(u)
90}
91
92fn unescape_octal_no_leading(c: char, queue: &mut VecDeque<char>) -> Option<char> {
93 let mut s = String::new();
94 s.push(c);
95 s.push(try_option!(queue.pop_front()));
96
97 let u = try_option!(u32::from_str_radix(&s, 8).ok());
98 char::from_u32(u)
99}