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 crate::payload_util::require_at_least;
12use alloc::vec::Vec;
13
14/// Mode-00 sub-commands.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16#[repr(u8)]
17#[allow(missing_docs)]
18pub enum XwrMode00 {
19    ReadMode = 0x01,
20    SetMode = 0x02,
21}
22
23/// Mode-01 (transparent smart card) sub-commands.
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25#[repr(u8)]
26#[allow(missing_docs)]
27pub enum XwrMode01 {
28    TransmitApdu = 0x01,
29    ConnectionDone = 0x02,
30    SecurePinEntry = 0x03,
31    SmartCardScan = 0x04,
32}
33
34/// `osdp_XWR` body — opaque container preserving mode + payload.
35///
36/// Higher-level helpers can interpret the payload according to `mode`.
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct XWrite {
39    /// XRW_MODE byte.
40    pub mode: u8,
41    /// Mode-specific payload (sub-command + data).
42    pub payload: Vec<u8>,
43}
44
45impl XWrite {
46    /// Encode.
47    pub fn encode(&self) -> Result<Vec<u8>, Error> {
48        let mut out = Vec::with_capacity(1 + self.payload.len());
49        out.push(self.mode);
50        out.extend_from_slice(&self.payload);
51        Ok(out)
52    }
53
54    /// Decode.
55    pub fn decode(data: &[u8]) -> Result<Self, Error> {
56        require_at_least(data, 1, 0xA1)?;
57        Ok(Self {
58            mode: data[0],
59            payload: data[1..].to_vec(),
60        })
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn roundtrip() {
70        let body = XWrite {
71            mode: 0x01,
72            payload: alloc::vec![0x02, 0xDE, 0xAD],
73        };
74        let bytes = body.encode().unwrap();
75        assert_eq!(bytes, [0x01, 0x02, 0xDE, 0xAD]);
76        assert_eq!(XWrite::decode(&bytes).unwrap(), body);
77    }
78
79    #[test]
80    fn mode_only_is_valid() {
81        let body = XWrite {
82            mode: 0x00,
83            payload: Vec::new(),
84        };
85        let bytes = body.encode().unwrap();
86        assert_eq!(bytes, [0x00]);
87        assert_eq!(XWrite::decode(&bytes).unwrap(), body);
88    }
89
90    #[test]
91    fn empty_decode_rejected() {
92        assert!(matches!(
93            XWrite::decode(&[]),
94            Err(Error::PayloadTooShort { code: 0xA1, .. })
95        ));
96    }
97}