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 | |
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 | |