zigbee_cluster_library/
frame.rs1use byte::{ctx, BytesExt, TryRead, TryWrite};
4
5use crate::header::ZclHeader;
6
7#[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 pub header: ZclHeader,
21 pub payload: &'a [u8],
23}
24
25#[allow(missing_docs)]
26pub struct ClusterSpecificCommand<'a> {
27 pub header: ZclHeader,
29 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 let input: &[u8] = &[
92 0x18, 0x01, 0x0a, 0x00, 0x00, 0x29, 0x3f, 0x0a, ];
97
98 let (frame, _) = ZclFrame::try_read(input, ()).expect("Failed to read ZclFrame");
100
101 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 let input: &[u8] = &[
120 0x19, 0x01, 0x0a, 0x00, 0x00, 0x29, 0x3f, 0x0a, ];
125
126 let (frame, _) = ZclFrame::try_read(input, ()).expect("Failed to read ZclFrame");
128
129 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}