Skip to main content

osdp/reply/
pdcap.rs

1//! `osdp_PDCAP` (`0x46`) — peripheral capabilities.
2//!
3//! # Spec: §7.5, Annex B
4//!
5//! Body is a sequence of [`Capability`] triplets.
6
7use crate::caps::{Capability, FunctionCode};
8use crate::error::Error;
9use alloc::vec::Vec;
10
11/// `osdp_PDCAP` body.
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct PdCap {
14    /// All advertised capabilities, in the order they appeared on the wire.
15    pub capabilities: Vec<Capability>,
16}
17
18impl PdCap {
19    /// New body.
20    pub fn new(capabilities: Vec<Capability>) -> Self {
21        Self { capabilities }
22    }
23
24    /// Look up the first capability matching `code`, if present.
25    pub fn capability(&self, code: FunctionCode) -> Option<Capability> {
26        self.capabilities
27            .iter()
28            .copied()
29            .find(|c| c.code == code.as_byte())
30    }
31
32    /// Encode.
33    pub fn encode(&self) -> Result<Vec<u8>, Error> {
34        let mut out = Vec::with_capacity(self.capabilities.len() * Capability::WIRE_LEN);
35        for c in &self.capabilities {
36            out.extend_from_slice(&c.encode());
37        }
38        Ok(out)
39    }
40
41    /// Decode.
42    pub fn decode(data: &[u8]) -> Result<Self, Error> {
43        if data.len() % Capability::WIRE_LEN != 0 {
44            return Err(Error::MalformedPayload {
45                code: 0x46,
46                reason: "PDCAP payload must be multiple of 3 bytes",
47            });
48        }
49        let capabilities = data
50            .chunks_exact(Capability::WIRE_LEN)
51            .map(|c| Capability::decode([c[0], c[1], c[2]]))
52            .collect();
53        Ok(Self { capabilities })
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60
61    #[test]
62    fn lookup_function_code() {
63        let body = PdCap::new(alloc::vec![
64            Capability {
65                code: FunctionCode::CommunicationSecurity.as_byte(),
66                compliance: 1,
67                number_of: 1,
68            },
69            Capability {
70                code: FunctionCode::ReceiveBufferSize.as_byte(),
71                compliance: 0x80,
72                number_of: 0,
73            },
74        ]);
75        let aes = body
76            .capability(FunctionCode::CommunicationSecurity)
77            .unwrap();
78        assert_eq!(aes.compliance, 1);
79        let rxsize = body.capability(FunctionCode::ReceiveBufferSize).unwrap();
80        assert_eq!(rxsize.u16_value(), 128);
81    }
82
83    #[test]
84    fn roundtrip() {
85        let body = PdCap::new(alloc::vec![
86            Capability {
87                code: 1,
88                compliance: 1,
89                number_of: 4
90            },
91            Capability {
92                code: 2,
93                compliance: 1,
94                number_of: 2
95            },
96        ]);
97        let bytes = body.encode().unwrap();
98        assert_eq!(bytes, [1, 1, 4, 2, 1, 2]);
99        let parsed = PdCap::decode(&bytes).unwrap();
100        assert_eq!(parsed, body);
101    }
102}