use core::fmt::{Display, Formatter, LowerHex, UpperHex};
use std::io::{self, Error, ErrorKind};
use std::iter::{Chain, Copied, Once, once};
use super::headers::data::Header;
use crate::MAX_PAYLOAD_SIZE;
use crate::protocol::Mask;
use crate::types::Payload;
use crate::utils::{HexSlice, WrappingU3};
use crate::validate::{CRC, Validate};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Data {
header: Header,
payload: Payload,
crc: u16,
}
impl Data {
const HEADER_SIZE: usize = 1;
const CRC_CHECKSUM_SIZE: usize = 2;
const METADATA_SIZE: usize = Self::HEADER_SIZE + Self::CRC_CHECKSUM_SIZE;
pub const MIN_PAYLOAD_SIZE: usize = 3;
pub const BUFFER_SIZE: usize = Self::METADATA_SIZE + MAX_PAYLOAD_SIZE;
#[must_use]
pub fn new(frame_num: WrappingU3, mut payload: Payload, ack_num: WrappingU3) -> Self {
let header = Header::new(frame_num, false, ack_num);
payload.mask();
Self {
header,
crc: calculate_crc(header.bits(), &payload),
payload,
}
}
#[must_use]
pub const fn frame_num(&self) -> WrappingU3 {
self.header.frame_num()
}
#[must_use]
pub const fn ack_num(&self) -> WrappingU3 {
self.header.ack_num()
}
#[must_use]
pub const fn is_retransmission(&self) -> bool {
self.header.contains(Header::RETRANSMIT)
}
pub fn set_is_retransmission(&mut self, is_retransmission: bool) {
self.header.set(Header::RETRANSMIT, is_retransmission);
self.crc = self.calculate_crc();
}
#[must_use]
pub fn into_payload(self) -> Payload {
self.payload
}
pub fn iter(&self) -> impl Iterator<Item = u8> {
self.into_iter()
}
}
impl Display for Data {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"DATA({}, {}, {})",
self.frame_num(),
self.ack_num(),
u8::from(self.is_retransmission())
)
}
}
impl Validate for Data {
fn crc(&self) -> u16 {
self.crc
}
fn calculate_crc(&self) -> u16 {
calculate_crc(self.header.bits(), &self.payload)
}
}
impl<'data> IntoIterator for &'data Data {
type Item = u8;
type IntoIter = Chain<
Chain<Once<u8>, Copied<<&'data Payload as IntoIterator>::IntoIter>>,
<[u8; 2] as IntoIterator>::IntoIter,
>;
fn into_iter(self) -> Self::IntoIter {
once(self.header.bits())
.chain(self.payload.iter().copied())
.chain(self.crc.to_be_bytes())
}
}
impl UpperHex for Data {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Data {{ header: ")?;
UpperHex::fmt(&self.header.bits(), f)?;
write!(f, ", payload: ")?;
UpperHex::fmt(&HexSlice::new(&self.payload), f)?;
write!(f, ", crc: ")?;
UpperHex::fmt(&HexSlice::new(&self.crc.to_be_bytes()), f)?;
write!(f, " }}")
}
}
impl LowerHex for Data {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Data {{ header: ")?;
LowerHex::fmt(&self.header.bits(), f)?;
write!(f, ", payload: ")?;
LowerHex::fmt(&HexSlice::new(&self.payload), f)?;
write!(f, ", crc: ")?;
LowerHex::fmt(&HexSlice::new(&self.crc.to_be_bytes()), f)?;
write!(f, " }}")
}
}
impl TryFrom<&[u8]> for Data {
type Error = Error;
fn try_from(buffer: &[u8]) -> io::Result<Self> {
let [header, payload @ .., crc0, crc1] = buffer else {
return Err(Error::new(
ErrorKind::UnexpectedEof,
"Too few bytes for DATA.",
));
};
if payload.len() < Self::MIN_PAYLOAD_SIZE {
return Err(Error::new(
ErrorKind::UnexpectedEof,
"Too few bytes for payload for DATA.",
));
}
Ok(Self {
header: Header::from_bits_retain(*header),
payload: payload.try_into().map_err(Error::other)?,
crc: u16::from_be_bytes([*crc0, *crc1]),
})
}
}
#[inline]
fn calculate_crc(header: u8, payload: &Payload) -> u16 {
let mut digest = CRC.digest();
digest.update(&[header]);
digest.update(payload);
digest.finalize()
}
#[cfg(test)]
#[expect(clippy::unwrap_used)]
mod tests {
use super::{Data, Header};
use crate::protocol::Mask;
use crate::types::RawFrame;
use crate::validate::{CRC, Validate};
#[test]
fn test_frame_num() {
let data = Data {
header: Header::from_bits_retain(0x25),
payload: [0x00, 0x00, 0x00, 0x02].as_slice().try_into().unwrap(),
crc: 0x1AAD,
};
assert_eq!(data.frame_num().as_u8(), 2);
let data = Data {
header: Header::from_bits_retain(0x53),
payload: [0x00, 0x80, 0x00, 0x02, 0x02, 0x11, 0x30]
.as_slice()
.try_into()
.unwrap(),
crc: 0x6316,
};
assert_eq!(data.frame_num().as_u8(), 5);
}
#[test]
fn test_ack_num() {
let data = Data {
header: Header::from_bits_retain(0x25),
payload: [0x00, 0x00, 0x00, 0x02].as_slice().try_into().unwrap(),
crc: 0x1AAD,
};
assert_eq!(data.ack_num().as_u8(), 5);
let data = Data {
header: Header::from_bits_retain(0x53),
payload: [0x00, 0x80, 0x00, 0x02, 0x02, 0x11, 0x30]
.as_slice()
.try_into()
.unwrap(),
crc: 0x6316,
};
assert_eq!(data.ack_num().as_u8(), 3);
}
#[test]
fn test_retransmit() {
let mut data = Data {
header: Header::from_bits_retain(0x25),
payload: [0x00, 0x00, 0x00, 0x02].as_slice().try_into().unwrap(),
crc: 0x1AAD,
};
assert!(!data.is_retransmission());
data.set_is_retransmission(true);
assert!(data.is_retransmission());
assert!(data.is_crc_valid());
let mut data = Data {
header: Header::from_bits_retain(0x53),
payload: [0x00, 0x80, 0x00, 0x02, 0x02, 0x11, 0x30]
.as_slice()
.try_into()
.unwrap(),
crc: 0x6316,
};
assert!(!data.is_retransmission());
data.set_is_retransmission(true);
assert!(data.is_retransmission());
assert!(data.is_crc_valid());
}
#[test]
fn test_to_string() {
let data = Data {
header: Header::from_bits_retain(0x25),
payload: [0x00, 0x00, 0x00, 0x02].as_slice().try_into().unwrap(),
crc: 0x1AAD,
};
assert_eq!(&data.to_string(), "DATA(2, 5, 0)");
let data = Data {
header: Header::from_bits_retain(0x53),
payload: [0x00, 0x80, 0x00, 0x02, 0x02, 0x11, 0x30]
.as_slice()
.try_into()
.unwrap(),
crc: 0x6316,
};
assert_eq!(&data.to_string(), "DATA(5, 3, 0)");
}
#[test]
fn test_crc() {
let data = Data {
header: Header::from_bits_retain(0x25),
payload: [0x00, 0x00, 0x00, 0x02].as_slice().try_into().unwrap(),
crc: 0x1AAD,
};
assert_eq!(data.crc(), 0x1AAD);
let data = Data {
header: Header::from_bits_retain(0x53),
payload: [0x00, 0x80, 0x00, 0x02, 0x02, 0x11, 0x30]
.as_slice()
.try_into()
.unwrap(),
crc: 0x6316,
};
assert_eq!(data.crc(), 0x6316);
}
#[test]
fn test_is_crc_valid() {
let data = Data {
header: Header::from_bits_retain(0x25),
payload: [0x00, 0x00, 0x00, 0x02].as_slice().try_into().unwrap(),
crc: 0x1AAD,
};
assert!(data.is_crc_valid());
let data = Data {
header: Header::from_bits_retain(0x53),
payload: [0x00, 0x80, 0x00, 0x02, 0x02, 0x11, 0x30]
.as_slice()
.try_into()
.unwrap(),
crc: 0x6316,
};
assert!(data.is_crc_valid());
}
#[test]
fn test_from_buffer() {
let buffer: Vec<u8> = vec![0x25, 0x00, 0x00, 0x00, 0x02, 0x1A, 0xAD];
let data = Data {
header: Header::from_bits_retain(0x25),
payload: [0x00, 0x00, 0x00, 0x02].as_slice().try_into().unwrap(),
crc: 0x1AAD,
};
assert_eq!(Data::try_from(buffer.as_slice()).unwrap(), data);
let buffer: Vec<u8> = vec![0x53, 0x00, 0x80, 0x00, 0x02, 0x02, 0x11, 0x30, 0x63, 0x16];
let data = Data {
header: Header::from_bits_retain(0x53),
payload: [0x00, 0x80, 0x00, 0x02, 0x02, 0x11, 0x30]
.as_slice()
.try_into()
.unwrap(),
crc: 0x6316,
};
assert_eq!(Data::try_from(buffer.as_slice()).unwrap(), data);
}
#[test]
fn test_data_frame() {
let header = 0x00;
let payload = [0x01, 0x00, 0x00, 0x04];
let mut masked_payload = payload;
masked_payload.mask();
let mut crc_target = vec![header];
crc_target.extend_from_slice(&masked_payload);
let crc = CRC.checksum(&crc_target);
let data = Data {
header: Header::from_bits_retain(0x00),
payload: masked_payload.as_slice().try_into().unwrap(),
crc,
};
let mut unmasked_payload: Vec<u8> = data.clone().into_payload().to_vec();
unmasked_payload.mask();
assert_eq!(unmasked_payload, payload);
let mut byte_representation = RawFrame::new();
byte_representation.extend(&data);
assert_eq!(&byte_representation, &[0, 67, 33, 168, 80, 155, 152]);
}
}