Skip to main content

osdp/reply/
xrd.rs

1//! `osdp_XRD` (`0xB1`) — extended-read response.
2//!
3//! # Spec: §7.26
4//!
5//! Body's first byte is `XRW_MODE` (matches the request); the remainder is
6//! mode-specific.
7
8use crate::error::Error;
9use crate::payload_util::require_at_least;
10use alloc::vec::Vec;
11
12/// `osdp_XRD` body — opaque container.
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct Xrd {
15    /// XRW_MODE byte.
16    pub mode: u8,
17    /// Mode-specific reply (sub-reply code + data).
18    pub payload: Vec<u8>,
19}
20
21impl Xrd {
22    /// Encode.
23    pub fn encode(&self) -> Result<Vec<u8>, Error> {
24        let mut out = Vec::with_capacity(1 + self.payload.len());
25        out.push(self.mode);
26        out.extend_from_slice(&self.payload);
27        Ok(out)
28    }
29
30    /// Decode.
31    pub fn decode(data: &[u8]) -> Result<Self, Error> {
32        require_at_least(data, 1, 0xB1)?;
33        Ok(Self {
34            mode: data[0],
35            payload: data[1..].to_vec(),
36        })
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn roundtrip() {
46        let body = Xrd {
47            mode: 0x01,
48            payload: alloc::vec![0xCA, 0xFE],
49        };
50        let bytes = body.encode().unwrap();
51        assert_eq!(bytes, [0x01, 0xCA, 0xFE]);
52        assert_eq!(Xrd::decode(&bytes).unwrap(), body);
53    }
54
55    #[test]
56    fn mode_only_is_valid() {
57        let body = Xrd {
58            mode: 0x00,
59            payload: Vec::new(),
60        };
61        let bytes = body.encode().unwrap();
62        assert_eq!(bytes, [0x00]);
63        assert_eq!(Xrd::decode(&bytes).unwrap(), body);
64    }
65
66    #[test]
67    fn empty_decode_rejected() {
68        assert!(matches!(
69            Xrd::decode(&[]),
70            Err(Error::PayloadTooShort { code: 0xB1, .. })
71        ));
72    }
73}