1macro_rules! ascii_control {
2 ($(($variant:ident, $value:expr)),* $(,)?) => {
3 #[repr(u8)]
5 pub enum AsciiControl {
6 $( $variant = $value, )*
7 }
8
9 impl std::fmt::Display for AsciiControl {
10 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
11 match self {
12 $( AsciiControl::$variant => write!(f, "<{}>", stringify!($variant).to_ascii_uppercase()), )*
13 }
14 }
15 }
16
17 impl std::fmt::Debug for AsciiControl {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 match self {
20 $( AsciiControl::$variant => write!(f, "<{}>", stringify!($variant).to_ascii_uppercase()), )*
21 }
22 }
23 }
24
25 impl TryFrom<u8> for AsciiControl {
26 type Error = ();
27 fn try_from(value: u8) -> Result<Self, Self::Error> {
28 $(
29 if value == $value {
30 return Ok(AsciiControl::$variant);
31 }
32 )*
33 Err(())
34 }
35 }
36
37 impl TryFrom<char> for AsciiControl {
38 type Error = ();
39 fn try_from(value: char) -> Result<Self, Self::Error> {
40 $(
41 if value == char::from($value) {
42 return Ok(AsciiControl::$variant);
43 }
44 )*
45 Err(())
46 }
47 }
48
49 impl std::str::FromStr for AsciiControl {
50 type Err = ();
51 fn from_str(s: &str) -> Result<Self, Self::Err> {
52 $(
53 if s.eq_ignore_ascii_case(stringify!($name)) {
54 return Ok(AsciiControl::$variant);
55 }
56 )*
57 Err(())
58 }
59 }
60 };
61}
62
63ascii_control! {
64 (Nul, 0),
65 (Soh, 1),
66 (Stx, 2),
67 (Etx, 3),
68 (Eot, 4),
69 (Enq, 5),
70 (Ack, 6),
71 (Bel, 7),
72 (Bs, 8),
73 (Tab, 9),
74 (Lf, 10),
75 (Vt, 11),
76 (Ff, 12),
77 (Cr, 13),
78 (So, 14 ),
79 (Si, 15),
80 (Dle, 16),
81 (Dc1, 17),
82 (Dc2, 18),
83 (Dc3, 19),
84 (Dc4, 20),
85 (Nak, 21),
86 (Syn, 22),
87 (Etb, 23),
88 (Can, 24),
89 (Em, 25),
90 (Sub, 26),
91 (Esc, 27),
92 (Fs, 28),
93 (Gs, 29),
94 (Rs, 30),
95 (Us, 31),
96 (Del, 127),
97}
98
99#[doc(hidden)]
100pub fn decode_string(input: &str) -> Vec<u8> {
101 let mut result = Vec::new();
102 let mut chars = input.chars().peekable();
103
104 while let Some(ch) = chars.next() {
105 if ch == '<' {
106 let mut control_name = String::new();
108 while let Some(ch) = chars.next() {
109 if ch == '>' {
110 break;
111 }
112 control_name.push(ch);
113 }
114
115 match control_name.to_uppercase().as_str() {
117 "NUL" => result.push(0),
118 "SOH" => result.push(1),
119 "STX" => result.push(2),
120 "ETX" => result.push(3),
121 "EOT" => result.push(4),
122 "ENQ" => result.push(5),
123 "ACK" => result.push(6),
124 "BEL" => result.push(7),
125 "BS" => result.push(8),
126 "TAB" => result.push(9),
127 "LF" => result.push(10),
128 "VT" => result.push(11),
129 "FF" => result.push(12),
130 "CR" => result.push(13),
131 "SO" => result.push(14),
132 "SI" => result.push(15),
133 "DLE" => result.push(16),
134 "DC1" => result.push(17),
135 "DC2" => result.push(18),
136 "DC3" => result.push(19),
137 "DC4" => result.push(20),
138 "NAK" => result.push(21),
139 "SYN" => result.push(22),
140 "ETB" => result.push(23),
141 "CAN" => result.push(24),
142 "EM" => result.push(25),
143 "SUB" => result.push(26),
144 "ESC" => result.push(27),
145 "FS" => result.push(28),
146 "GS" => result.push(29),
147 "RS" => result.push(30),
148 "US" => result.push(31),
149 "DEL" => result.push(127),
150 _ => {
151 result.push(b'<');
153 result.extend_from_slice(control_name.as_bytes());
154 result.push(b'>');
155 }
156 }
157 } else {
158 let mut buf = [0; 4];
160 let char_bytes = ch.encode_utf8(&mut buf);
161 result.extend_from_slice(char_bytes.as_bytes());
162 }
163 }
164
165 result
166}
167
168#[doc(hidden)]
169pub fn encode_string(bytes: &[u8]) -> String {
170 use std::fmt::Write;
171 let mut s = String::new();
172 for chunk in bytes.utf8_chunks() {
173 for c in chunk.valid().chars() {
174 if let Ok(c) = AsciiControl::try_from(c) {
175 write!(s, "{}", c).unwrap();
176 } else {
177 write!(s, "{}", c).unwrap();
178 }
179 }
180 if !chunk.invalid().is_empty() {
181 write!(s, "<{}>", hex::encode(chunk.invalid())).unwrap();
182 }
183 }
184 s
185}