use core::fmt::{Display, Formatter, LowerHex, UpperHex};
use std::io::{self, Error, ErrorKind};
use std::iter::{Chain, Once, Peekable, once};
use std::vec::Drain;
use super::headers::ack::Header;
use crate::hex_slice::HexSlice;
use crate::seq::Seq;
use crate::validate::{CRC, Validate};
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Ack {
header: Header,
crc: u16,
}
impl Ack {
#[must_use]
pub const fn new(ack_num: Seq, n_rdy: bool) -> Self {
let header = Header::new(ack_num, n_rdy);
Self {
header,
crc: CRC.checksum(&[header.bits()]),
}
}
#[must_use]
pub const fn not_ready(self) -> bool {
self.header.contains(Header::NOT_READY)
}
#[must_use]
pub fn ack_num(self) -> Seq {
self.header.ack_num()
}
}
impl Display for Ack {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"ACK({}){}",
self.ack_num(),
if self.not_ready() { '-' } else { '+' }
)
}
}
impl Validate for Ack {
fn crc(&self) -> u16 {
self.crc
}
fn calculate_crc(&self) -> u16 {
CRC.checksum(&[self.header.bits()])
}
}
impl IntoIterator for Ack {
type Item = u8;
type IntoIter = Chain<Once<u8>, <[u8; 2] as IntoIterator>::IntoIter>;
fn into_iter(self) -> Self::IntoIter {
once(self.header.bits()).chain(self.crc.to_be_bytes())
}
}
impl TryFrom<Peekable<Drain<'_, u8>>> for Ack {
type Error = Error;
fn try_from(mut buffer: Peekable<Drain<'_, u8>>) -> io::Result<Self> {
let header = buffer
.next()
.ok_or_else(|| Error::from(ErrorKind::UnexpectedEof))?;
let crc0 = buffer
.next()
.ok_or_else(|| Error::from(ErrorKind::UnexpectedEof))?;
let crc1 = buffer
.next()
.ok_or_else(|| Error::from(ErrorKind::UnexpectedEof))?;
Ok(Self {
header: Header::from_bits_retain(header),
crc: u16::from_be_bytes([crc0, crc1]),
})
}
}
impl UpperHex for Ack {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Ack {{ header: ")?;
UpperHex::fmt(&self.header.bits(), f)?;
write!(f, ", crc: ")?;
UpperHex::fmt(&HexSlice::new(&self.crc.to_be_bytes()), f)?;
write!(f, " }}")
}
}
impl LowerHex for Ack {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Ack {{ header: ")?;
LowerHex::fmt(&self.header.bits(), f)?;
write!(f, ", crc: ")?;
LowerHex::fmt(&HexSlice::new(&self.crc.to_be_bytes()), f)?;
write!(f, " }}")
}
}
#[cfg(test)]
mod tests {
use super::{Ack, Header};
use crate::validate::Validate;
const ACK1: Ack = Ack {
header: Header::from_bits_retain(0x81),
crc: 0x6059,
};
const ACK2: Ack = Ack {
header: Header::from_bits_retain(0x8E),
crc: 0x91B6,
};
#[test]
fn test_ready() {
assert!(!ACK1.not_ready());
assert!(ACK2.not_ready());
}
#[test]
fn test_ack_num() {
assert_eq!(ACK1.ack_num().as_u8(), 1);
assert_eq!(ACK2.ack_num().as_u8(), 6);
}
#[test]
fn test_to_string() {
assert_eq!(&ACK1.to_string(), "ACK(1)+");
assert_eq!(&ACK2.to_string(), "ACK(6)-");
}
#[test]
fn test_header() {
assert_eq!(ACK1.header, Header::from_bits_retain(0x81));
assert_eq!(ACK2.header, Header::from_bits_retain(0x8E));
}
#[test]
fn test_crc() {
assert_eq!(ACK1.crc(), 0x6059);
assert_eq!(ACK2.crc(), 0x91B6);
}
#[test]
fn test_is_crc_valid() {
assert!(ACK1.validate().is_ok());
assert!(ACK2.validate().is_ok());
}
#[test]
fn test_from_buffer() {
let mut buffer1: Vec<u8> = vec![0x81, 0x60, 0x59];
assert_eq!(
Ack::try_from(buffer1.drain(..).peekable())
.expect("Reference frame should be a valid ACK"),
ACK1
);
let mut buffer2: Vec<u8> = vec![0x8E, 0x91, 0xB6];
assert_eq!(
Ack::try_from(buffer2.drain(..).peekable())
.expect("Reference frame should be a valid ACK"),
ACK2
);
}
}