ham_cats/whisker/
destination.rs1use core::str::FromStr;
2
3use arrayvec::ArrayString;
4
5use crate::identity::Identity;
6
7#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
8pub struct AckData {
9 number: u8,
10 pub is_ack: bool,
11}
12
13impl AckData {
14 pub fn new(number: u8, is_ack: bool) -> Self {
16 Self::try_new(number, is_ack).expect("Invalid ack number given")
17 }
18
19 pub fn try_new(number: u8, is_ack: bool) -> Option<Self> {
21 if (1..=127).contains(&number) {
22 Some(Self { number, is_ack })
23 } else {
24 None
25 }
26 }
27
28 pub fn number(self) -> u8 {
29 self.number
30 }
31
32 pub fn set_ack(&mut self) {
33 self.is_ack = true;
34 }
35}
36
37#[derive(Debug, PartialEq, Eq, Clone)]
38pub struct Destination {
39 ack_data: Option<AckData>,
40 callsign: ArrayString<253>,
41 ssid: u8,
42}
43
44impl Destination {
45 pub fn new(dest_callsign: &str, dest_ssid: u8, ack_data: Option<AckData>) -> Option<Self> {
47 let callsign = ArrayString::from_str(dest_callsign).ok()?;
48 let ssid = dest_ssid;
49
50 Some(Self {
51 ack_data,
52 callsign,
53 ssid,
54 })
55 }
56
57 pub fn ack_data(&self) -> Option<AckData> {
58 self.ack_data
59 }
60
61 pub fn callsign(&self) -> &str {
62 self.callsign.as_str()
63 }
64
65 pub fn ssid(&self) -> u8 {
66 self.ssid
67 }
68
69 pub fn as_identity(&self) -> Identity<'_> {
70 Identity::new(&self.callsign, self.ssid)
71 }
72
73 pub fn encode<'a>(&self, buf: &'a mut [u8]) -> Option<&'a [u8]> {
74 let n = self.callsign.len() + 2;
75 let ack = match self.ack_data {
76 Some(ack_data) => {
77 if ack_data.is_ack {
78 (1 << 7) | ack_data.number
79 } else {
80 ack_data.number
81 }
82 }
83 None => 0,
84 };
85
86 *buf.get_mut(0)? = n.try_into().unwrap();
87 *buf.get_mut(1)? = ack;
88 buf.get_mut(2..n)?.copy_from_slice(self.callsign.as_bytes());
89 *buf.get_mut(n)? = self.ssid;
90
91 Some(&buf[0..(n + 1)])
92 }
93
94 pub fn decode(data: &[u8]) -> Option<Self> {
95 let n = data.first()?;
96 let call_length: usize = n.checked_sub(2)?.into();
97
98 let ack = *data.get(1)?;
99 if ack == 0b10000000 {
100 return None;
102 }
103 let ack_data = if ack == 0 {
104 None
105 } else {
106 Some(AckData::new(ack & 0x7F, ack & 0x80 > 0))
107 };
108
109 let callsign = core::str::from_utf8(data.get(2..(call_length + 2))?).ok()?;
110 let callsign = ArrayString::from_str(callsign).ok()?;
111
112 let ssid = *data.get(call_length + 2)?;
113
114 Some(Self {
115 ack_data,
116 callsign,
117 ssid,
118 })
119 }
120}