1use crate::error::Error;
10use crate::payload_util::require_at_least;
11use alloc::vec::Vec;
12
13macro_rules! oui_plus_payload {
14 ($Ty:ident, $code:expr, $doc:literal) => {
15 #[doc = $doc]
16 #[derive(Debug, Clone, PartialEq, Eq)]
17 pub struct $Ty {
18 pub oui: [u8; 3],
20 pub payload: Vec<u8>,
22 }
23
24 impl $Ty {
25 pub fn encode(&self) -> Result<Vec<u8>, Error> {
27 let mut out = Vec::with_capacity(3 + self.payload.len());
28 out.extend_from_slice(&self.oui);
29 out.extend_from_slice(&self.payload);
30 Ok(out)
31 }
32
33 pub fn decode(data: &[u8]) -> Result<Self, Error> {
35 require_at_least(data, 3, $code)?;
36 let mut oui = [0u8; 3];
37 oui.copy_from_slice(&data[..3]);
38 Ok(Self {
39 oui,
40 payload: data[3..].to_vec(),
41 })
42 }
43 }
44 };
45}
46
47oui_plus_payload!(
48 MfgStatR,
49 0x83,
50 "`osdp_MFGSTATR` body — manufacturer status reply."
51);
52oui_plus_payload!(
53 MfgErrR,
54 0x84,
55 "`osdp_MFGERRR` body — manufacturer error reply."
56);
57oui_plus_payload!(MfgRep, 0x90, "`osdp_MFGREP` body — manufacturer reply.");
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn mfgstatr_roundtrip() {
65 let body = MfgStatR {
66 oui: [0x00, 0x06, 0x8E],
67 payload: alloc::vec![0x01, 0x02],
68 };
69 let bytes = body.encode().unwrap();
70 assert_eq!(bytes, [0x00, 0x06, 0x8E, 0x01, 0x02]);
71 assert_eq!(MfgStatR::decode(&bytes).unwrap(), body);
72 }
73
74 #[test]
75 fn mfgstatr_rejects_short_oui() {
76 assert!(matches!(
77 MfgStatR::decode(&[0x00, 0x06]),
78 Err(Error::PayloadTooShort { code: 0x83, .. })
79 ));
80 }
81
82 #[test]
83 fn mfgerrr_roundtrip() {
84 let body = MfgErrR {
85 oui: [0xAA, 0xBB, 0xCC],
86 payload: alloc::vec![0xEE],
87 };
88 let bytes = body.encode().unwrap();
89 assert_eq!(bytes, [0xAA, 0xBB, 0xCC, 0xEE]);
90 assert_eq!(MfgErrR::decode(&bytes).unwrap(), body);
91 }
92
93 #[test]
94 fn mfgerrr_rejects_short_oui() {
95 assert!(matches!(
96 MfgErrR::decode(&[0xAA]),
97 Err(Error::PayloadTooShort { code: 0x84, .. })
98 ));
99 }
100
101 #[test]
102 fn mfgrep_roundtrip_empty_payload() {
103 let body = MfgRep {
104 oui: [0x11, 0x22, 0x33],
105 payload: Vec::new(),
106 };
107 let bytes = body.encode().unwrap();
108 assert_eq!(bytes, [0x11, 0x22, 0x33]);
109 assert_eq!(MfgRep::decode(&bytes).unwrap(), body);
110 }
111
112 #[test]
113 fn mfgrep_rejects_short_oui() {
114 assert!(matches!(
115 MfgRep::decode(&[]),
116 Err(Error::PayloadTooShort { code: 0x90, .. })
117 ));
118 }
119}