1#[derive(Clone, Copy, Eq, PartialEq)]
2enum 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
13pub 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