1pub const DEFAULT_GROUP: [u8; 4] = [239, 255, 134, 121];
8pub const DEFAULT_PORT: u16 = 15056;
10pub const DEFAULT_INGRESS_LIMIT: u16 = 120;
12pub const GROUP_LEN: usize = 6;
14
15#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
17#[repr(u8)]
18pub enum State {
19 #[default]
21 Disabled = 0,
22 Active = 1,
24 NoNetwork = 2,
27}
28
29impl State {
30 pub const fn decode(value: u8) -> Option<Self> {
31 Some(match value {
32 0 => Self::Disabled,
33 1 => Self::Active,
34 2 => Self::NoNetwork,
35 _ => return None,
36 })
37 }
38}
39
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub struct Group {
45 pub address: [u8; 4],
46 pub port: u16,
47}
48
49impl Default for Group {
50 fn default() -> Self {
51 Self {
52 address: DEFAULT_GROUP,
53 port: DEFAULT_PORT,
54 }
55 }
56}
57
58impl Group {
59 pub fn encode(self) -> [u8; GROUP_LEN] {
60 let [a, b, c, d] = self.address;
61 let [lo, hi] = self.port.to_le_bytes();
62 [a, b, c, d, lo, hi]
63 }
64
65 pub fn is_multicast(self) -> bool {
68 self.address[0] & 0xF0 == 0xE0
69 }
70
71 pub fn decode(value: &[u8]) -> Option<Self> {
74 let &[a, b, c, d, lo, hi] = value else {
75 return None;
76 };
77 let port = u16::from_le_bytes([lo, hi]);
78 let address = [a, b, c, d];
79 (address != [0; 4] && address != [255; 4] && port != 0).then_some(Self { address, port })
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn a_group_round_trips_and_a_unicast_peer_stands_in_for_one() {
89 let group = Group::default();
90 assert_eq!(group.encode(), [239, 255, 134, 121, 0xD0, 0x3A]);
91 assert_eq!(Group::decode(&group.encode()), Some(group));
92 assert!(group.is_multicast());
93 let peer = Group::decode(&[192, 168, 1, 1, 0xD0, 0x3A]).unwrap();
94 assert!(!peer.is_multicast());
95 assert_eq!(Group::decode(&[0, 0, 0, 0, 0xD0, 0x3A]), None);
96 assert_eq!(Group::decode(&[255, 255, 255, 255, 0xD0, 0x3A]), None);
97 assert_eq!(Group::decode(&[239, 255, 134, 121, 0, 0]), None);
98 assert_eq!(Group::decode(&[239, 255, 134, 121, 1]), None);
99 }
100}