Skip to main content

apis_saltans_aps/frame/
control.rs

1use bitflags::bitflags;
2use le_stream::{FromLeStream, ToLeStream};
3
4pub use self::delivery_mode::DeliveryMode;
5pub use self::frame_type::FrameType;
6use crate::Extended;
7use crate::frame::destination::WeakDestination;
8
9mod delivery_mode;
10mod frame_type;
11
12/// APS frame control field.
13#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, FromLeStream, ToLeStream)]
14#[repr(transparent)]
15pub struct Control(u8);
16
17bitflags! {
18    impl Control: u8 {
19        /// Frame type mask.
20        const FRAME_TYPE = 0b1100_0000;
21
22        /// Delivery mode mask.
23        const DELIVERY_MODE = 0b0011_0000;
24
25        /// Indicate if the frame is a command frame.
26        const ACK_FORMAT = 0b0000_1000;
27
28        /// Security provider flag.
29        const SECURITY = 0b0000_0100;
30
31        /// Acknowledgment request flag.
32        const ACK_REQUEST = 0b0000_0010;
33
34        /// Extended header flag.
35        const EXTENDED_HEADER = 0b0000_0001;
36    }
37}
38
39impl core::fmt::Display for Control {
40    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
41        bitflags::parser::to_writer(self, formatter)
42    }
43}
44
45impl core::str::FromStr for Control {
46    type Err = bitflags::parser::ParseError;
47
48    fn from_str(flags: &str) -> Result<Self, Self::Err> {
49        bitflags::parser::from_str(flags)
50    }
51}
52
53impl Control {
54    /// Return the frame type.
55    #[must_use]
56    pub fn frame_type(self) -> FrameType {
57        FrameType::try_from(
58            (self & Self::FRAME_TYPE).bits() >> Self::FRAME_TYPE.bits().trailing_zeros(),
59        )
60        .unwrap_or_else(|_| unreachable!("Frame type covers all possible values."))
61    }
62
63    /// Set the frame type.
64    pub const fn set_frame_type(&mut self, frame_type: FrameType) {
65        self.0 = (self.bits() & !Self::FRAME_TYPE.bits())
66            | ((frame_type as u8) << Self::FRAME_TYPE.bits().trailing_zeros());
67    }
68
69    /// Return the delivery mode.
70    #[must_use]
71    pub fn delivery_mode(self) -> Option<DeliveryMode> {
72        DeliveryMode::try_from(
73            (self & Self::DELIVERY_MODE).bits() >> Self::DELIVERY_MODE.bits().trailing_zeros(),
74        )
75        .ok()
76    }
77
78    /// Set the delivery mode.
79    pub const fn set_delivery_mode(&mut self, delivery_mode: DeliveryMode) {
80        self.0 = (self.bits() & !Self::DELIVERY_MODE.bits())
81            | ((delivery_mode as u8) << Self::DELIVERY_MODE.bits().trailing_zeros());
82    }
83
84    /// Set the delivery mode based on the destination type.
85    pub const fn set_destination(&mut self, destination: WeakDestination) {
86        match destination {
87            WeakDestination::Unicast(_) => {
88                self.set_delivery_mode(DeliveryMode::Unicast);
89            }
90            WeakDestination::Broadcast(_) => {
91                self.set_delivery_mode(DeliveryMode::Broadcast);
92            }
93            WeakDestination::Group(_) => {
94                self.set_delivery_mode(DeliveryMode::Group);
95            }
96        }
97    }
98
99    /// Set whether an extended header is present.
100    pub fn set_extended_header(&mut self, extended_header_present: bool) {
101        if extended_header_present {
102            self.insert(Self::EXTENDED_HEADER);
103        } else {
104            self.remove(Self::EXTENDED_HEADER);
105        }
106    }
107
108    pub(crate) fn deserialize_extended_header<T>(self, mut bytes: T) -> Result<Option<Extended>, ()>
109    where
110        T: Iterator<Item = u8>,
111    {
112        if self.contains(Self::EXTENDED_HEADER) {
113            let Some(extended) = Extended::from_le_stream(
114                matches!(self.frame_type(), FrameType::Acknowledgment),
115                &mut bytes,
116            ) else {
117                return Err(());
118            };
119
120            Ok(Some(extended))
121        } else {
122            Ok(None)
123        }
124    }
125
126    pub(crate) fn deserialize_destination<T>(self, mut bytes: T) -> Option<WeakDestination>
127    where
128        T: Iterator<Item = u8>,
129    {
130        self.delivery_mode()
131            .and_then(|delivery_mode| match delivery_mode {
132                DeliveryMode::Unicast => {
133                    u8::from_le_stream(&mut bytes).map(WeakDestination::Unicast)
134                }
135                DeliveryMode::Broadcast => {
136                    u8::from_le_stream(&mut bytes).map(WeakDestination::Broadcast)
137                }
138                DeliveryMode::Group => u16::from_le_stream(&mut bytes).map(WeakDestination::Group),
139            })
140    }
141}