Skip to main content

osdp/command/
xwrite.rs

1//! `osdp_XWR` (`0xA1`) — extended write.
2//!
3//! # Spec: §5.11, §6.20, Table 5
4//!
5//! Body's first byte is `XRW_MODE`. Subsequent layout depends on the mode:
6//!
7//! - `0x00` (default): generic mode-control sub-commands.
8//! - `0x01` (transparent smart card): APDU transmission, secure PIN entry.
9
10use crate::error::Error;
11use alloc::vec::Vec;
12
13/// Mode-00 sub-commands.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[repr(u8)]
16#[allow(missing_docs)]
17pub enum XwrMode00 {
18    ReadMode = 0x01,
19    SetMode = 0x02,
20}
21
22/// Mode-01 (transparent smart card) sub-commands.
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24#[repr(u8)]
25#[allow(missing_docs)]
26pub enum XwrMode01 {
27    TransmitApdu = 0x01,
28    ConnectionDone = 0x02,
29    SecurePinEntry = 0x03,
30    SmartCardScan = 0x04,
31}
32
33/// `osdp_XWR` body — opaque container preserving mode + payload.
34///
35/// Higher-level helpers can interpret the payload according to `mode`.
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct XWrite {
38    /// XRW_MODE byte.
39    pub mode: u8,
40    /// Mode-specific payload (sub-command + data).
41    pub payload: Vec<u8>,
42}
43
44impl XWrite {
45    /// Encode.
46    pub fn encode(&self) -> Result<Vec<u8>, Error> {
47        let mut out = Vec::with_capacity(1 + self.payload.len());
48        out.push(self.mode);
49        out.extend_from_slice(&self.payload);
50        Ok(out)
51    }
52
53    /// Decode.
54    pub fn decode(data: &[u8]) -> Result<Self, Error> {
55        if data.is_empty() {
56            return Err(Error::MalformedPayload {
57                code: 0xA1,
58                reason: "XWR requires XRW_MODE byte",
59            });
60        }
61        Ok(Self {
62            mode: data[0],
63            payload: data[1..].to_vec(),
64        })
65    }
66}