1use crate::frame::{self, Cmd, Frame, PropPayload};
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub struct TidAllocator {
12 next: u8,
13}
14
15impl TidAllocator {
16 pub const fn new() -> Self {
18 Self { next: 1 }
19 }
20
21 pub fn allocate(&mut self) -> u8 {
23 let tid = self.next;
24 self.next = if tid >= frame::TID_MAX { 1 } else { tid + 1 };
25 tid
26 }
27}
28
29impl Default for TidAllocator {
30 fn default() -> Self {
31 Self::new()
32 }
33}
34
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
37pub enum PropertyNotificationKind {
38 Is,
39 Inserted,
40 Removed,
41}
42
43impl PropertyNotificationKind {
44 pub const fn from_command(command: Cmd) -> Option<Self> {
45 match command {
46 Cmd::PropIs => Some(Self::Is),
47 Cmd::PropInserted => Some(Self::Inserted),
48 Cmd::PropRemoved => Some(Self::Removed),
49 _ => None,
50 }
51 }
52}
53
54#[derive(Clone, Copy, Debug, PartialEq, Eq)]
56pub struct PropertyNotification<'a> {
57 pub tid: u8,
58 pub kind: PropertyNotificationKind,
59 pub key: u32,
60 pub value: &'a [u8],
61}
62
63impl<'a> PropertyNotification<'a> {
64 pub fn parse(bytes: &'a [u8]) -> Result<Self, PropertyNotificationError> {
67 let frame = Frame::parse(bytes).map_err(|_| PropertyNotificationError::MalformedFrame)?;
68 Self::from_frame(&frame)
69 }
70
71 pub fn from_frame(frame: &Frame<'a>) -> Result<Self, PropertyNotificationError> {
72 let kind = frame
73 .command()
74 .and_then(PropertyNotificationKind::from_command)
75 .ok_or(PropertyNotificationError::UnexpectedCommand)?;
76 let payload = PropPayload::parse(frame.payload)
77 .map_err(|_| PropertyNotificationError::MalformedPayload)?;
78 Ok(Self {
79 tid: frame.header.tid(),
80 kind,
81 key: payload.key,
82 value: payload.value,
83 })
84 }
85}
86
87#[derive(Clone, Copy, Debug, PartialEq, Eq)]
88pub enum PropertyNotificationError {
89 MalformedFrame,
90 UnexpectedCommand,
91 MalformedPayload,
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn transaction_ids_never_allocate_zero() {
100 let mut allocator = TidAllocator::new();
101 let ids: [u8; 10] = core::array::from_fn(|_| allocator.allocate());
102 assert_eq!(ids, [1, 2, 3, 4, 5, 6, 7, 1, 2, 3]);
103 }
104
105 #[test]
106 fn parses_property_notification() {
107 let mut bytes = [0; 16];
108 let len = frame::prop_is(&mut bytes, 3, 0x1234, &[5, 6]).unwrap();
109 assert_eq!(
110 PropertyNotification::parse(&bytes[..len]).unwrap(),
111 PropertyNotification {
112 tid: 3,
113 kind: PropertyNotificationKind::Is,
114 key: 0x1234,
115 value: &[5, 6],
116 }
117 );
118 }
119
120 #[test]
121 fn rejects_non_property_command() {
122 let mut bytes = [0; 8];
123 let len = frame::save(&mut bytes, 1).unwrap();
124 assert_eq!(
125 PropertyNotification::parse(&bytes[..len]),
126 Err(PropertyNotificationError::UnexpectedCommand)
127 );
128 }
129}