use core::fmt::{Display, UpperHex};
use std::io::{Error, ErrorKind, Read, Write};
use log::{debug, trace, warn};
use crate::frame::Frame;
use crate::protocol::{CANCEL, FLAG, SUBSTITUTE, Stuffing, WAKE, X_OFF, X_ON};
use crate::types::RawFrame;
use crate::utils::HexSlice;
use crate::validate::Validate;
#[derive(Debug)]
pub struct FrameBuffer<T> {
inner: T,
buffer: RawFrame,
}
impl<T> FrameBuffer<T> {
#[must_use]
pub const fn new(inner: T) -> Self {
Self {
inner,
buffer: RawFrame::new(),
}
}
#[must_use]
pub fn into_inner(self) -> T {
self.inner
}
#[must_use]
fn buffer_overflow(byte: u8) -> Error {
Error::other(format!("Frame buffer overflow: {byte:#04X}"))
}
}
impl<T> FrameBuffer<T>
where
T: Read,
{
pub fn read_frame(&mut self) -> std::io::Result<Frame> {
let frame: Frame = self.read_raw_frame()?.try_into()?;
if frame.is_crc_valid() {
Ok(frame)
} else {
Err(Error::new(
ErrorKind::InvalidData,
"Frame is not CRC valid.",
))
}
}
pub fn read_raw_frame(&mut self) -> std::io::Result<&[u8]> {
self.buffer.clear();
let mut error = false;
#[allow(clippy::unbuffered_bytes)]
for byte in (&mut self.inner).bytes() {
match byte? {
CANCEL => {
trace!("Resetting buffer due to cancel byte.");
self.buffer.clear();
error = false;
}
FLAG => {
trace!("Received flag byte.");
if !error && !self.buffer.is_empty() {
debug!("Received frame.");
trace!("Buffer: {:#04X}", HexSlice::new(&self.buffer));
self.buffer.unstuff();
trace!("Unstuffed buffer: {:#04X}", HexSlice::new(&self.buffer));
return Ok(&self.buffer);
}
trace!("Resetting buffer due to error or empty buffer.");
trace!("Error condition was: {error}");
trace!("Buffer: {:#04X}", HexSlice::new(&self.buffer));
self.buffer.clear();
error = false;
}
SUBSTITUTE => {
trace!("Received SUBSTITUTE byte. Setting error condition.");
error = true;
}
X_ON => {
warn!("NCP requested to resume transmission. Ignoring.");
}
X_OFF => {
warn!("NCP requested to stop transmission. Ignoring.");
}
WAKE => {
if self.buffer.is_empty() {
debug!("NCP tried to wake us up.");
} else if self.buffer.push(WAKE).is_err() {
return Err(Self::buffer_overflow(WAKE));
}
}
byte => {
if self.buffer.push(byte).is_err() {
return Err(Self::buffer_overflow(byte));
}
}
}
}
Err(Error::new(
ErrorKind::UnexpectedEof,
"Byte stream terminated unexpectedly.",
))
}
}
impl<T> FrameBuffer<T>
where
T: Write,
{
pub fn write_frame<F>(&mut self, frame: F) -> std::io::Result<()>
where
F: IntoIterator<Item = u8> + Display + UpperHex,
{
debug!("Writing frame: {frame}");
trace!("Frame: {frame:#04X}");
self.buffer.clear();
self.buffer.extend(frame);
trace!("Frame bytes: {:#04X}", HexSlice::new(&self.buffer));
self.buffer.stuff()?;
trace!("Stuffed bytes: {:#04X}", HexSlice::new(&self.buffer));
self.buffer
.push(FLAG)
.map_err(|_| Self::buffer_overflow(FLAG))?;
trace!("Writing bytes: {:#04X}", HexSlice::new(&self.buffer));
self.inner.write_all(&self.buffer)?;
self.inner.flush()
}
}