ham_cats/whisker/
destination.rs

1use 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    /// Panics if the given ack number is invalid (i.e. not between 1-127, inclusive)
15    pub fn new(number: u8, is_ack: bool) -> Self {
16        Self::try_new(number, is_ack).expect("Invalid ack number given")
17    }
18
19    /// Returns `None` if the given ack number is invalid (i.e. not between 1-127, inclusive)
20    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    /// Returns none if the dest callsign is too long
46    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            // is_ack is true and # is 0
101            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}