zigbee_cluster_library/
frame.rs

1//! General ZCL Frame
2
3use byte::{ctx, BytesExt, TryRead, TryWrite};
4
5use crate::header::ZclHeader;
6
7/// ZCL Frame
8///
9/// See Section 2.4.1
10#[allow(missing_docs)]
11pub enum ZclFrame<'a> {
12    GeneralCommand(GeneralCommand<'a>),
13    ClusterSpecificCommand(ClusterSpecificCommand<'a>),
14    Reserved(ZclHeader),
15}
16
17#[allow(missing_docs)]
18pub struct GeneralCommand<'a> {
19    /// ZCL Header
20    pub header: ZclHeader,
21    /// ZCL Payload
22    pub payload: &'a [u8],
23}
24
25#[allow(missing_docs)]
26pub struct ClusterSpecificCommand<'a> {
27    /// ZCL Header
28    pub header: ZclHeader,
29    /// ZCL Payload
30    pub payload: &'a [u8],
31}
32
33impl<'a> TryRead<'a, ()> for ZclFrame<'a> {
34    fn try_read(bytes: &'a [u8], _: ()) -> byte::Result<(Self, usize)> {
35        let offset = &mut 0;
36
37        let header: ZclHeader = bytes.read_with(offset, ())?;
38        let frame = match header.frame_control.frame_type() {
39            crate::header::frame_control::FrameType::GlobalCommand => {
40                let payload = bytes.read_with(offset, ctx::Bytes::Len(bytes.len() - *offset))?;
41
42                Self::GeneralCommand(GeneralCommand { header, payload })
43            }
44            crate::header::frame_control::FrameType::ClusterCommand => {
45                let payload = bytes.read_with(offset, ctx::Bytes::Len(bytes.len() - *offset))?;
46
47                Self::ClusterSpecificCommand(ClusterSpecificCommand { header, payload })
48            }
49            crate::header::frame_control::FrameType::Reserved => Self::Reserved(header),
50        };
51
52        Ok((frame, *offset))
53    }
54}
55
56impl TryWrite for ZclFrame<'_> {
57    fn try_write(self, bytes: &mut [u8], _: ()) -> byte::Result<usize> {
58        let offset = &mut 0;
59        match self {
60            ZclFrame::GeneralCommand(general_command) => {
61                bytes.write_with(offset, general_command.header, ())?;
62                bytes.write(offset, general_command.payload)?;
63
64                Ok(*offset)
65            }
66            ZclFrame::ClusterSpecificCommand(cluster_specific_command) => {
67                bytes.write_with(offset, cluster_specific_command.header, ())?;
68                bytes.write(offset, cluster_specific_command.payload)?;
69
70                Ok(*offset)
71            }
72            ZclFrame::Reserved(zcl_header) => {
73                bytes.write_with(offset, zcl_header, ())?;
74
75                Ok(*offset)
76            }
77        }
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use byte::TryRead;
84
85    use super::ZclFrame;
86
87    #[allow(clippy::panic)]
88    #[test]
89    fn zcl_command() {
90        // given
91        let input: &[u8] = &[
92            0x18, // frame control
93            0x01, // sequence number
94            0x0a, // command
95            0x00, 0x00, 0x29, 0x3f, 0x0a, // payload
96        ];
97
98        // when
99        let (frame, _) = ZclFrame::try_read(input, ()).expect("Failed to read ZclFrame");
100
101        // then
102        let expected = &[0x00, 0x00, 0x29, 0x3f, 0x0a];
103        assert!(matches!(frame, ZclFrame::GeneralCommand(_)));
104        if let ZclFrame::GeneralCommand(general_command) = frame {
105            assert!(!general_command
106                .header
107                .frame_control
108                .is_manufacturer_specific());
109            assert_eq!(general_command.payload, expected);
110        } else {
111            panic!("GeneralCommand expecyed!");
112        }
113    }
114
115    #[allow(clippy::panic)]
116    #[test]
117    fn cluster_specific_command() {
118        // given
119        let input: &[u8] = &[
120            0x19, // frame control
121            0x01, // sequence number
122            0x0a, // command
123            0x00, 0x00, 0x29, 0x3f, 0x0a, // payload
124        ];
125
126        // when
127        let (frame, _) = ZclFrame::try_read(input, ()).expect("Failed to read ZclFrame");
128
129        // then
130        let expected = &[0x00, 0x00, 0x29, 0x3f, 0x0a];
131        assert!(matches!(frame, ZclFrame::ClusterSpecificCommand(_)));
132        if let ZclFrame::ClusterSpecificCommand(general_command) = frame {
133            assert!(!general_command
134                .header
135                .frame_control
136                .is_manufacturer_specific());
137            assert_eq!(general_command.payload, expected);
138        } else {
139            panic!("ClusterSpecificCommand expecyed!");
140        }
141    }
142}