1use std::collections::HashMap;
2enum HidCharacter {
3 Shift,
4 CarriageReturn,
5 Character(char),
6 Unknown,
7}
8
9fn decode_char(char: u8, shift: bool) -> HidCharacter {
10 if 0x28 == char {
11 return HidCharacter::CarriageReturn;
12 } else if 0x02 == char {
13 return HidCharacter::Shift;
14 }
15
16 let hid: HashMap<u8, char> = [
17 (4, 'a'),
18 (5, 'b'),
19 (6, 'c'),
20 (7, 'd'),
21 (8, 'e'),
22 (9, 'f'),
23 (10, 'g'),
24 (11, 'h'),
25 (12, 'i'),
26 (13, 'j'),
27 (14, 'k'),
28 (15, 'l'),
29 (16, 'm'),
30 (17, 'n'),
31 (18, 'o'),
32 (19, 'p'),
33 (20, 'q'),
34 (21, 'r'),
35 (22, 's'),
36 (23, 't'),
37 (24, 'u'),
38 (25, 'v'),
39 (26, 'w'),
40 (27, 'x'),
41 (28, 'y'),
42 (29, 'z'),
43 (30, '1'),
44 (31, '2'),
45 (32, '3'),
46 (33, '4'),
47 (34, '5'),
48 (35, '6'),
49 (36, '7'),
50 (37, '8'),
51 (38, '9'),
52 (39, '0'),
53 (44, ' '),
54 (45, '-'),
55 (46, '='),
56 (47, '['),
57 (48, ']'),
58 (49, '\\'),
59 (51, ';'),
60 (52, '\''),
61 (53, '~'),
62 (54, ','),
63 (55, '.'),
64 (56, '/'),
65 ]
66 .iter()
67 .cloned()
68 .collect();
69
70 let hid2: HashMap<u8, char> = [
71 (4, 'A'),
72 (5, 'B'),
73 (6, 'C'),
74 (7, 'D'),
75 (8, 'E'),
76 (9, 'F'),
77 (10, 'G'),
78 (11, 'H'),
79 (12, 'I'),
80 (13, 'J'),
81 (14, 'K'),
82 (15, 'L'),
83 (16, 'M'),
84 (17, 'N'),
85 (18, 'O'),
86 (19, 'P'),
87 (20, 'Q'),
88 (21, 'R'),
89 (22, 'S'),
90 (23, 'T'),
91 (24, 'U'),
92 (25, 'V'),
93 (26, 'W'),
94 (27, 'X'),
95 (28, 'Y'),
96 (29, 'Z'),
97 (30, '!'),
98 (31, '@'),
99 (32, '#'),
100 (33, '$'),
101 (34, '%'),
102 (35, '^'),
103 (36, '&'),
104 (37, '*'),
105 (38, '('),
106 (39, ')'),
107 (44, ' '),
108 (45, '_'),
109 (46, '+'),
110 (47, '{'),
111 (48, '}'),
112 (49, '|'),
113 (51, ':'),
114 (52, '"'),
115 (53, '~'),
116 (54, '<'),
117 (55, '>'),
118 (56, '?'),
119 ]
120 .iter()
121 .cloned()
122 .collect();
123
124 let map = if shift { &hid2 } else { &hid };
125
126 match map.get(&char) {
127 Some(c) => HidCharacter::Character(*c),
128 None => HidCharacter::Unknown,
129 }
130}
131
132pub fn decode(card: &[u8]) -> Result<String, String> {
133 let mut result = String::new();
134 let mut shift = false;
135 for character in card.iter() {
136 let character = *character;
137 if character != 0 {
138 match decode_char(character, shift) {
139 HidCharacter::Shift => {
140 shift = !shift;
141 }
142 HidCharacter::CarriageReturn => return Ok(result),
143 HidCharacter::Character(c) => {
144 result.push_str(&format!("{}", c));
145 }
146 HidCharacter::Unknown => {
147 return Err(format!("Unknown character: {}", character));
148 }
149 }
150 }
151 }
152 Err("No carriage return found".to_string())
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn valid_card() {
161 const CARD: [u8; 88] = [
162 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00,
163 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00,
164 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
165 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
166 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00,
167 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00,
168 0x00, 0x00, 0x00, 0x00,
169 ];
170 let result = decode(&CARD);
171 assert_eq!(result, Ok("0002530785".to_string()));
172 }
173 #[test]
174 fn invalid_card() {
175 const CARD: [u8; 88] = [
176 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00,
177 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00,
178 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
179 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
180 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00,
181 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00,
182 0x00, 0x00, 0x00, 0x00,
183 ];
184 let result = decode(&CARD);
185 assert_eq!(result, Err("Unknown character: 3".to_string()));
186 }
187 #[test]
188 fn no_carriage_return() {
189 const CARD: [u8; 88] = [
190 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00,
191 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00,
192 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00,
193 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
194 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24,
195 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00,
196 0x00, 0x00, 0x00, 0x00,
197 ];
198 let result = decode(&CARD);
199 assert_eq!(result, Err("No carriage return found".to_string()));
200 }
201}