use core::str::FromStr;
use arrayvec::ArrayString;
use crate::identity::Identity;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct AckData {
number: u8,
pub is_ack: bool,
}
impl AckData {
pub fn new(number: u8, is_ack: bool) -> Self {
Self::try_new(number, is_ack).expect("Invalid ack number given")
}
pub fn try_new(number: u8, is_ack: bool) -> Option<Self> {
if (1..=127).contains(&number) {
Some(Self { number, is_ack })
} else {
None
}
}
pub fn number(self) -> u8 {
self.number
}
pub fn set_ack(&mut self) {
self.is_ack = true;
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Destination {
ack_data: Option<AckData>,
callsign: ArrayString<253>,
ssid: u8,
}
impl Destination {
pub fn new(dest_callsign: &str, dest_ssid: u8, ack_data: Option<AckData>) -> Option<Self> {
let callsign = ArrayString::from_str(dest_callsign).ok()?;
let ssid = dest_ssid;
Some(Self {
ack_data,
callsign,
ssid,
})
}
pub fn ack_data(&self) -> Option<AckData> {
self.ack_data
}
pub fn callsign(&self) -> &str {
self.callsign.as_str()
}
pub fn ssid(&self) -> u8 {
self.ssid
}
pub fn as_identity(&self) -> Identity<'_> {
Identity::new(&self.callsign, self.ssid)
}
pub fn encode<'a>(&self, buf: &'a mut [u8]) -> Option<&'a [u8]> {
let n = self.callsign.len() + 2;
let ack = match self.ack_data {
Some(ack_data) => {
if ack_data.is_ack {
(1 << 7) | ack_data.number
} else {
ack_data.number
}
}
None => 0,
};
*buf.get_mut(0)? = n.try_into().unwrap();
*buf.get_mut(1)? = ack;
buf.get_mut(2..n)?.copy_from_slice(self.callsign.as_bytes());
*buf.get_mut(n)? = self.ssid;
Some(&buf[0..(n + 1)])
}
pub fn decode(data: &[u8]) -> Option<Self> {
let n = data.first()?;
let call_length: usize = n.checked_sub(2)?.into();
let ack = *data.get(1)?;
if ack == 0b10000000 {
return None;
}
let ack_data = if ack == 0 {
None
} else {
Some(AckData::new(ack & 0x7F, ack & 0x80 > 0))
};
let callsign = core::str::from_utf8(data.get(2..(call_length + 2))?).ok()?;
let callsign = ArrayString::from_str(callsign).ok()?;
let ssid = *data.get(call_length + 2)?;
Some(Self {
ack_data,
callsign,
ssid,
})
}
}