zigbee 0.1.0-alpha.3

ZigBee protocol stack in `no-std` based on the ZigBee specification 22 1.0
Documentation
/// Implements `byte` for a struct.
#[doc(hidden)]
#[macro_export]
macro_rules! impl_byte {
    (
        $(#[$m:meta])*
        $v:vis struct $name:ident($vt:vis $ty:ty);
    ) => {
        $(#[$m])*
        $v struct $name($vt $ty);

        impl<C: ::core::default::Default> ::byte::TryRead<'_, C> for $name {
            fn try_read(bytes: &'_ [u8], _: C) -> ::byte::Result<(Self, usize)> {
                let (v, sz) = <$ty>::try_read(bytes, ::byte::LE)?;
                Ok((Self(v), sz))
            }
        }

        impl<C: ::core::default::Default> ::byte::TryWrite<C> for $name {
            fn try_write(self, bytes: &mut [u8], _: C) -> ::byte::Result<usize> {
                self.0.try_write(bytes, ::byte::LE)
            }
        }
    };
    (
        $(#[$m:meta])*
        $v:vis enum $name:ident {
            $(
                $(#[doc = $doc:literal])*
                $(#[fallback = $fallback:literal])?
                $variant:ident $(= $value:expr)?
            ),+
            $(,)?
         }
    ) => {
        $(#[$m])*
        $v enum $name {
            $(
                $(#[doc = $doc])*
                $variant $(= $value)?
            ),+
        }

        impl<C: ::core::default::Default> ::byte::TryRead<'_, C> for $name {
            fn try_read(bytes: &'_ [u8], _: C) -> ::byte::Result<(Self, usize)> {
                use ::byte::BytesExt;
                let offset = &mut 0;
                let id: u8 = bytes.read(offset)?;
                let variant = match id {
                    $(
                        $($value => Self::$variant)?
                        $(
                            _ => {
                                let _ = $fallback;
                                Self::$variant
                            },
                        )?
                    ),+
                };
                Ok((variant, *offset))
            }
        }

        impl<C: ::core::default::Default> ::byte::TryWrite<C> for $name {
            fn try_write(self, bytes: &mut [u8], _: C) -> ::byte::Result<usize> {
                use ::byte::BytesExt;
                let offset = &mut 0;
                bytes.write_with(offset, self as u8, ::byte::LE)?;
                Ok(*offset)
            }
        }
    };
    (
        $(#[$m:meta])*
        $v:vis struct $name:ident $(<$lifetime:lifetime>)? {
            $(
                $(#[doc = $doc:literal])*
                $(#[ctx = $ctx_hdr:expr])?
                $(#[ctx_write = $ctx_write:expr])?
                $(#[parse_if = $parse_if_hdr:expr])?
                $vf:vis $field_name:ident: $field_ty:ty
            ),+
            $(,)?
        }
    ) => {
        $(#[$m])*
        $v struct $name $(<$lifetime>)? {
            $(
                $(#[doc = $doc])*
                $vf $field_name: $field_ty
            ),+
        }

        #[allow(single_use_lifetimes, clippy::redundant_closure_call, unreachable_code, unused_variables)]
        impl<'a, C: ::core::default::Default> ::byte::TryRead<'a, C> for $name $(<$lifetime>)? {
            fn try_read(bytes: &'a [u8], _: C) -> ::byte::Result<(Self, usize)> {
                use ::byte::BytesExt;
                let offset = &mut 0;
                $(
                    let ctx = ::byte::LE;
                    $(
                        let ctx = $ctx_hdr;
                    )?

                    let should_read = true;
                    $(let should_read = $parse_if_hdr;)?

                    let $field_name: $field_ty = if should_read {
                        let v = bytes.read_with(offset, ctx)?;
                        $(
                            let _ = $parse_if_hdr;
                            let v = Some(v);
                        )?
                        v
                    } else {
                        (|| {
                            $(
                                let _ = $parse_if_hdr;
                                return None;
                            )?
                            unreachable!()
                        })()
                    };
                )+
                let s = Self {
                    $($field_name,)+
                };
                Ok((s, *offset))
            }
        }

        #[allow(single_use_lifetimes, unused_variables)]
        impl<'a, C: ::core::default::Default> ::byte::TryWrite<C> for $name $(<$lifetime>)? {
            fn try_write(self, bytes: &mut [u8], _: C) -> ::byte::Result<usize> {
                use ::byte::BytesExt;
                let offset = &mut 0;

                let Self {
                    $($field_name,)+
                } = self;

                $(
                    let ctx = ::byte::LE;
                    $(
                        let ctx = $ctx_hdr;
                    )?
                    $(
                        let ctx = $ctx_write;
                    )?

                    let should_write = true;
                    $(let should_write = $parse_if_hdr;)?
                    if should_write {
                        let v = $field_name;
                        $(
                            let _ = $parse_if_hdr;
                            let v = v.unwrap();
                        )?
                        bytes.write_with(offset, v, ctx)?;
                    }
                )+
                Ok(*offset)
            }
        }
    }
}
pub use impl_byte;

#[cfg(test)]
mod tests {
    use byte::TryRead;
    use byte::TryWrite;

    use crate::internal::types::ShortAddress;

    impl_byte! {
        struct DataFrame<'a> {
            flag: u8,
            address: ShortAddress,
            #[parse_if = flag > 0]
            opt: Option<u16>,
            length: u8,
            #[ctx = byte::ctx::Bytes::Len(usize::from(length))]
            #[ctx_write = ()]
            data: &'a [u8],
        }
    }

    #[test]
    fn parse() {
        let bytes = &[0x01, 0x12, 0xff, 0x11, 0x22, 0x4, 0xaa, 0xaa, 0xaa, 0xaa];

        let (frame, len) = DataFrame::try_read(bytes, ()).unwrap();

        assert_eq!(len, 10);
        assert_eq!(frame.flag, 0x01);
        assert_eq!(frame.address, ShortAddress(0xff12));
        assert_eq!(frame.opt, Some(0x2211));
        assert_eq!(frame.length, 0x04);
        assert_eq!(frame.data, &[0xaa, 0xaa, 0xaa, 0xaa]);

        let mut buf = [0u8; 10];
        frame.try_write(&mut buf, ()).unwrap();
        assert_eq!(&buf, bytes);
    }

    impl_byte! {
        #[derive(Debug,PartialEq, Eq)]
        #[repr(u8)]
        enum Command {
            Data = 0x01,
            Payload = 0x02,
            #[fallback = true]
            Reserved,
        }
    }

    #[test]
    fn parse_enum() {
        let bytes = &[0x02];
        let (command, len) = Command::try_read(bytes, ()).unwrap();
        assert_eq!(len, 1);
        assert_eq!(command, Command::Payload);
    }
}