1 #[derive(Clone, Copy, Eq, PartialEq)]
2 enum State {
3     /// The state after seeing a `\`.
4     Escape,
5     /// The state after seeing a `\x`.
6     HexFirst,
7     /// The state after seeing a `\x[0-9A-Fa-f]`.
8     HexSecond(char),
9     /// Default state.
10     Literal,
11 }
12 
unescape(s: &str) -> Vec<u8>13 pub fn unescape(s: &str) -> Vec<u8> {
14     use self::State::*;
15 
16     let mut bytes = vec![];
17     let mut state = Literal;
18     for c in s.chars() {
19         match state {
20             Escape => match c {
21                 '\\' => {
22                     bytes.push(b'\\');
23                     state = Literal;
24                 }
25                 'n' => {
26                     bytes.push(b'\n');
27                     state = Literal;
28                 }
29                 'r' => {
30                     bytes.push(b'\r');
31                     state = Literal;
32                 }
33                 't' => {
34                     bytes.push(b'\t');
35                     state = Literal;
36                 }
37                 'x' => {
38                     state = HexFirst;
39                 }
40                 c => {
41                     bytes.extend(format!(r"\{}", c).into_bytes());
42                     state = Literal;
43                 }
44             },
45             HexFirst => match c {
46                 '0'..='9' | 'A'..='F' | 'a'..='f' => {
47                     state = HexSecond(c);
48                 }
49                 c => {
50                     bytes.extend(format!(r"\x{}", c).into_bytes());
51                     state = Literal;
52                 }
53             },
54             HexSecond(first) => match c {
55                 '0'..='9' | 'A'..='F' | 'a'..='f' => {
56                     let ordinal = format!("{}{}", first, c);
57                     let byte = u8::from_str_radix(&ordinal, 16).unwrap();
58                     bytes.push(byte);
59                     state = Literal;
60                 }
61                 c => {
62                     let original = format!(r"\x{}{}", first, c);
63                     bytes.extend(original.into_bytes());
64                     state = Literal;
65                 }
66             },
67             Literal => match c {
68                 '\\' => {
69                     state = Escape;
70                 }
71                 c => {
72                     bytes.extend(c.to_string().as_bytes());
73                 }
74             },
75         }
76     }
77     match state {
78         Escape => bytes.push(b'\\'),
79         HexFirst => bytes.extend(b"\\x"),
80         HexSecond(c) => bytes.extend(format!("\\x{}", c).into_bytes()),
81         Literal => {}
82     }
83     bytes
84 }
85