zigbee_cluster_library/common/
parse.rs

1/// Implements `byte` for a struct.
2#[macro_export]
3macro_rules! impl_byte {
4    (
5        $(#[$m:meta])*
6        $v:vis struct $name:ident($vt:vis $ty:ty);
7    ) => {
8        $(#[$m])*
9        $v struct $name($vt $ty);
10
11        impl<C: ::core::default::Default> ::byte::TryRead<'_, C> for $name {
12            fn try_read(bytes: &'_ [u8], _: C) -> ::byte::Result<(Self, usize)> {
13                let (v, sz) = <$ty>::try_read(bytes, ::byte::LE)?;
14                Ok((Self(v), sz))
15            }
16        }
17
18        impl<C: ::core::default::Default> ::byte::TryWrite<C> for $name {
19            fn try_write(self, bytes: &mut [u8], _: C) -> ::byte::Result<usize> {
20                self.0.try_write(bytes, ::byte::LE)
21            }
22        }
23    };
24    (
25        $(#[$m:meta])*
26        $v:vis struct $name:ident $(<$lifetime:lifetime>)? {
27            $(
28                $(#[doc = $doc:literal])*
29                $(#[ctx = $ctx_hdr:expr])?
30                $(#[parse_if = $parse_if_hdr:expr])?
31                $vf:vis $field_name:ident: $field_ty:ty
32            ),+
33            $(,)?
34        }
35    ) => {
36        $(#[$m])*
37        $v struct $name $(<$lifetime>)? {
38            $(
39                $(#[doc = $doc])*
40                $vf $field_name: $field_ty
41            ),+
42        }
43
44        #[allow(single_use_lifetimes, clippy::redundant_closure_call, unreachable_code, unused_variables)]
45        impl<'a, C: ::core::default::Default> ::byte::TryRead<'a, C> for $name $(<$lifetime>)? {
46            fn try_read(bytes: &'a [u8], _: C) -> ::byte::Result<(Self, usize)> {
47                use ::byte::BytesExt;
48                let offset = &mut 0;
49                $(
50                    let ctx = ::byte::LE;
51                    $(
52                        let ctx = $ctx_hdr;
53                    )?
54
55                    let should_read = true;
56                    $(let should_read = $parse_if_hdr;)?
57
58                    let $field_name: $field_ty = if should_read {
59                        let v = bytes.read_with(offset, ctx)?;
60                        $(
61                            let _ = $parse_if_hdr;
62                            let v = Some(v);
63                        )?
64                        v
65                    } else {
66                        (|| {
67                            $(
68                                let _ = $parse_if_hdr;
69                                return None;
70                            )?
71                            unreachable!()
72                        })()
73                    };
74                )+
75                let s = Self {
76                    $($field_name,)+
77                };
78                Ok((s, *offset))
79            }
80        }
81
82        #[allow(single_use_lifetimes, unused_variables)]
83        impl<'a, C: ::core::default::Default> ::byte::TryWrite<C> for $name $(<$lifetime>)? {
84            fn try_write(self, bytes: &mut [u8], _: C) -> ::byte::Result<usize> {
85                use ::byte::BytesExt;
86                let offset = &mut 0;
87
88                let Self {
89                    $($field_name,)+
90                } = self;
91
92                $(
93                    let ctx = ::byte::LE;
94                    $(
95                        let _ = $ctx_hdr;
96                        let ctx = ();
97                    )?
98
99                    let should_write = true;
100                    $(let should_write = $parse_if_hdr;)?
101                    if should_write {
102                        let v = $field_name;
103                        $(
104                            let _ = $parse_if_hdr;
105                            let v = v.unwrap();
106                        )?
107                        bytes.write_with(offset, v, ctx)?;
108                    }
109                )+
110                Ok(*offset)
111            }
112        }
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use byte::TryRead;
119    use byte::TryWrite;
120
121    impl_byte! {
122        struct DataFrame<'a> {
123            flag: u8,
124            #[parse_if = flag > 0]
125            opt: Option<u16>,
126            length: u8,
127            #[ctx = byte::ctx::Bytes::Len(usize::from(length))]
128            data: &'a [u8],
129        }
130    }
131
132    #[test]
133    fn parse() {
134        let bytes = &[0x01, 0x11, 0x22, 0x4, 0xaa, 0xaa, 0xaa, 0xaa];
135
136        let (frame, len) =
137            DataFrame::try_read(bytes, ()).expect("Could not read DataFrame in test");
138
139        assert_eq!(len, 8);
140        assert_eq!(frame.flag, 0x01);
141        assert_eq!(frame.opt, Some(0x2211));
142        assert_eq!(frame.length, 0x04);
143        assert_eq!(frame.data, &[0xaa, 0xaa, 0xaa, 0xaa]);
144
145        let mut buf = [0u8; 8];
146        frame
147            .try_write(&mut buf, ())
148            .expect("Could not write DataFrame in test");
149        assert_eq!(&buf, bytes);
150    }
151}