unescape/
lib.rs

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
14// Takes in a string with backslash escapes written out with literal backslash characters and
15// converts it to a string with the proper escaped characters.
16pub 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}