1#[cfg(feature = "alloc")]
10use alloc::vec::Vec;
11
12use crate::error::Error;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16#[repr(u8)]
17#[allow(missing_docs)]
18pub enum CommandCode {
19 Poll = 0x60,
20 Id = 0x61,
21 Cap = 0x62,
22 LStat = 0x64,
23 IStat = 0x65,
24 OStat = 0x66,
25 RStat = 0x67,
26 Out = 0x68,
27 Led = 0x69,
28 Buz = 0x6A,
29 Text = 0x6B,
30 ComSet = 0x6E,
31 BioRead = 0x73,
32 BioMatch = 0x74,
33 KeySet = 0x75,
34 Chlng = 0x76,
35 SCrypt = 0x77,
36 AcuRxSize = 0x7B,
37 FileTransfer = 0x7C,
38 Mfg = 0x80,
39 XWrite = 0xA1,
40 Abort = 0xA2,
41 PivData = 0xA3,
42 GenAuth = 0xA4,
43 CrAuth = 0xA5,
44 KeepActive = 0xA7,
45}
46
47impl CommandCode {
48 pub const fn from_byte(b: u8) -> Result<Self, Error> {
50 Ok(match b {
51 0x60 => Self::Poll,
52 0x61 => Self::Id,
53 0x62 => Self::Cap,
54 0x64 => Self::LStat,
55 0x65 => Self::IStat,
56 0x66 => Self::OStat,
57 0x67 => Self::RStat,
58 0x68 => Self::Out,
59 0x69 => Self::Led,
60 0x6A => Self::Buz,
61 0x6B => Self::Text,
62 0x6E => Self::ComSet,
63 0x73 => Self::BioRead,
64 0x74 => Self::BioMatch,
65 0x75 => Self::KeySet,
66 0x76 => Self::Chlng,
67 0x77 => Self::SCrypt,
68 0x7B => Self::AcuRxSize,
69 0x7C => Self::FileTransfer,
70 0x80 => Self::Mfg,
71 0xA1 => Self::XWrite,
72 0xA2 => Self::Abort,
73 0xA3 => Self::PivData,
74 0xA4 => Self::GenAuth,
75 0xA5 => Self::CrAuth,
76 0xA7 => Self::KeepActive,
77 other => return Err(Error::UnknownCommand(other)),
78 })
79 }
80
81 pub const fn as_byte(self) -> u8 {
83 self as u8
84 }
85}
86
87pub mod abort;
88pub mod acu_rx_size;
89pub mod biometric;
90pub mod buzzer;
91pub mod cap;
92pub mod chlng;
93pub mod comset;
94pub mod file_transfer;
95pub mod id;
96pub mod keep_active;
97pub mod keyset;
98pub mod led;
99pub mod local_status;
100pub mod mfg;
101pub mod output;
102pub mod piv;
103pub mod poll;
104pub mod scrypt;
105pub mod text;
106pub mod xwrite;
107
108pub use abort::Abort;
109pub use acu_rx_size::AcuRxSize;
110pub use biometric::{BioFormat, BioMatch, BioRead, BioType};
111pub use buzzer::{BuzzerControl, BuzzerTone};
112pub use cap::Cap;
113pub use chlng::Chlng;
114pub use comset::ComSet;
115pub use file_transfer::FileTransfer;
116pub use id::Id;
117pub use keep_active::KeepActive;
118pub use keyset::KeySet;
119pub use led::{LedColor, LedControl, LedPermanent, LedTemporary};
120pub use local_status::{InputStatus, LocalStatus, OutputStatus, ReaderStatus};
121pub use mfg::Mfg;
122pub use output::{OutputControl, OutputControlCode};
123pub use piv::{CrAuth, GenAuth, PivData};
124pub use poll::Poll;
125pub use scrypt::SCrypt;
126pub use text::{Text, TextCommand};
127pub use xwrite::{XWrite, XwrMode00, XwrMode01};
128
129#[cfg(feature = "alloc")]
134#[derive(Debug, Clone, PartialEq, Eq)]
135#[non_exhaustive]
136#[allow(missing_docs)]
137pub enum Command {
138 Poll(Poll),
139 Id(Id),
140 Cap(Cap),
141 LocalStatus(LocalStatus),
142 InputStatus(InputStatus),
143 OutputStatus(OutputStatus),
144 ReaderStatus(ReaderStatus),
145 Output(OutputControl),
146 Led(LedControl),
147 Buzzer(BuzzerControl),
148 Text(Text),
149 ComSet(ComSet),
150 BioRead(BioRead),
151 BioMatch(BioMatch),
152 KeySet(KeySet),
153 Chlng(Chlng),
154 SCrypt(SCrypt),
155 AcuRxSize(AcuRxSize),
156 FileTransfer(FileTransfer),
157 Mfg(Mfg),
158 XWrite(XWrite),
159 Abort(Abort),
160 PivData(PivData),
161 GenAuth(GenAuth),
162 CrAuth(CrAuth),
163 KeepActive(KeepActive),
164}
165
166#[cfg(feature = "alloc")]
167impl Command {
168 pub fn code(&self) -> CommandCode {
170 match self {
171 Command::Poll(_) => CommandCode::Poll,
172 Command::Id(_) => CommandCode::Id,
173 Command::Cap(_) => CommandCode::Cap,
174 Command::LocalStatus(_) => CommandCode::LStat,
175 Command::InputStatus(_) => CommandCode::IStat,
176 Command::OutputStatus(_) => CommandCode::OStat,
177 Command::ReaderStatus(_) => CommandCode::RStat,
178 Command::Output(_) => CommandCode::Out,
179 Command::Led(_) => CommandCode::Led,
180 Command::Buzzer(_) => CommandCode::Buz,
181 Command::Text(_) => CommandCode::Text,
182 Command::ComSet(_) => CommandCode::ComSet,
183 Command::BioRead(_) => CommandCode::BioRead,
184 Command::BioMatch(_) => CommandCode::BioMatch,
185 Command::KeySet(_) => CommandCode::KeySet,
186 Command::Chlng(_) => CommandCode::Chlng,
187 Command::SCrypt(_) => CommandCode::SCrypt,
188 Command::AcuRxSize(_) => CommandCode::AcuRxSize,
189 Command::FileTransfer(_) => CommandCode::FileTransfer,
190 Command::Mfg(_) => CommandCode::Mfg,
191 Command::XWrite(_) => CommandCode::XWrite,
192 Command::Abort(_) => CommandCode::Abort,
193 Command::PivData(_) => CommandCode::PivData,
194 Command::GenAuth(_) => CommandCode::GenAuth,
195 Command::CrAuth(_) => CommandCode::CrAuth,
196 Command::KeepActive(_) => CommandCode::KeepActive,
197 }
198 }
199
200 pub fn encode_data(&self) -> Result<Vec<u8>, Error> {
202 match self {
203 Command::Poll(c) => c.encode(),
204 Command::Id(c) => c.encode(),
205 Command::Cap(c) => c.encode(),
206 Command::LocalStatus(c) => c.encode(),
207 Command::InputStatus(c) => c.encode(),
208 Command::OutputStatus(c) => c.encode(),
209 Command::ReaderStatus(c) => c.encode(),
210 Command::Output(c) => c.encode(),
211 Command::Led(c) => c.encode(),
212 Command::Buzzer(c) => c.encode(),
213 Command::Text(c) => c.encode(),
214 Command::ComSet(c) => c.encode(),
215 Command::BioRead(c) => c.encode(),
216 Command::BioMatch(c) => c.encode(),
217 Command::KeySet(c) => c.encode(),
218 Command::Chlng(c) => c.encode(),
219 Command::SCrypt(c) => c.encode(),
220 Command::AcuRxSize(c) => c.encode(),
221 Command::FileTransfer(c) => c.encode(),
222 Command::Mfg(c) => c.encode(),
223 Command::XWrite(c) => c.encode(),
224 Command::Abort(c) => c.encode(),
225 Command::PivData(c) => c.encode(),
226 Command::GenAuth(c) => c.encode(),
227 Command::CrAuth(c) => c.encode(),
228 Command::KeepActive(c) => c.encode(),
229 }
230 }
231
232 pub fn decode(code: CommandCode, data: &[u8]) -> Result<Self, Error> {
234 Ok(match code {
235 CommandCode::Poll => Command::Poll(Poll::decode(data)?),
236 CommandCode::Id => Command::Id(Id::decode(data)?),
237 CommandCode::Cap => Command::Cap(Cap::decode(data)?),
238 CommandCode::LStat => Command::LocalStatus(LocalStatus::decode(data)?),
239 CommandCode::IStat => Command::InputStatus(InputStatus::decode(data)?),
240 CommandCode::OStat => Command::OutputStatus(OutputStatus::decode(data)?),
241 CommandCode::RStat => Command::ReaderStatus(ReaderStatus::decode(data)?),
242 CommandCode::Out => Command::Output(OutputControl::decode(data)?),
243 CommandCode::Led => Command::Led(LedControl::decode(data)?),
244 CommandCode::Buz => Command::Buzzer(BuzzerControl::decode(data)?),
245 CommandCode::Text => Command::Text(Text::decode(data)?),
246 CommandCode::ComSet => Command::ComSet(ComSet::decode(data)?),
247 CommandCode::BioRead => Command::BioRead(BioRead::decode(data)?),
248 CommandCode::BioMatch => Command::BioMatch(BioMatch::decode(data)?),
249 CommandCode::KeySet => Command::KeySet(KeySet::decode(data)?),
250 CommandCode::Chlng => Command::Chlng(Chlng::decode(data)?),
251 CommandCode::SCrypt => Command::SCrypt(SCrypt::decode(data)?),
252 CommandCode::AcuRxSize => Command::AcuRxSize(AcuRxSize::decode(data)?),
253 CommandCode::FileTransfer => Command::FileTransfer(FileTransfer::decode(data)?),
254 CommandCode::Mfg => Command::Mfg(Mfg::decode(data)?),
255 CommandCode::XWrite => Command::XWrite(XWrite::decode(data)?),
256 CommandCode::Abort => Command::Abort(Abort::decode(data)?),
257 CommandCode::PivData => Command::PivData(PivData::decode(data)?),
258 CommandCode::GenAuth => Command::GenAuth(GenAuth::decode(data)?),
259 CommandCode::CrAuth => Command::CrAuth(CrAuth::decode(data)?),
260 CommandCode::KeepActive => Command::KeepActive(KeepActive::decode(data)?),
261 })
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use super::*;
268
269 #[test]
270 fn code_roundtrip() {
271 for byte in [
272 0x60u8, 0x61, 0x62, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6E, 0x73, 0x74,
273 0x75, 0x76, 0x77, 0x7B, 0x7C, 0x80, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA7,
274 ] {
275 assert_eq!(CommandCode::from_byte(byte).unwrap().as_byte(), byte);
276 }
277 }
278
279 #[test]
280 fn unknown_byte_errors() {
281 assert!(matches!(
282 CommandCode::from_byte(0xFF),
283 Err(Error::UnknownCommand(0xFF))
284 ));
285 }
286}