ashv2 9.0.0

Implementation of the Asynchronous Serial Host (ASH) protocol.
Documentation
//! Frame types and their respective headers for the `ASHv2` protocol.

use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex};
use std::io::{self, ErrorKind};
use std::vec::Drain;

pub use self::ack::Ack;
pub use self::data::Data;
pub use self::error::Error;
pub use self::nak::Nak;
pub use self::rst::{RST, Rst};
pub use self::rst_ack::RstAck;
use crate::validate::Validate;

mod ack;
mod data;
mod error;
pub mod headers;
mod nak;
mod rst;
mod rst_ack;

/// Available frame types.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Frame {
    /// `ACK` frame
    Ack(Ack),

    /// `DATA` frame
    Data(Box<Data>),

    /// `ERROR` frame
    Error(Error),

    /// `NAK` frame
    Nak(Nak),

    /// `RST` frame
    Rst(Rst),

    /// `RST_ACK` frame
    RstAck(RstAck),
}

impl Display for Frame {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Ack(ack) => Display::fmt(ack, f),
            Self::Data(data) => Display::fmt(data, f),
            Self::Error(error) => Display::fmt(error, f),
            Self::Nak(nak) => Display::fmt(nak, f),
            Self::Rst(rst) => Display::fmt(rst, f),
            Self::RstAck(rst_ack) => Display::fmt(rst_ack, f),
        }
    }
}

impl TryFrom<Drain<'_, u8>> for Frame {
    type Error = io::Error;

    fn try_from(buffer: Drain<'_, u8>) -> io::Result<Self> {
        let mut buffer = buffer.peekable();
        match *buffer
            .peek()
            .ok_or_else(|| io::Error::new(ErrorKind::UnexpectedEof, "Missing frame header."))?
        {
            Rst::HEADER => Rst::try_from(buffer).map(Self::Rst),
            RstAck::HEADER => RstAck::try_from(buffer).map(Self::RstAck),
            Error::HEADER => Error::try_from(buffer).map(Self::Error),
            header if header & 0x80 == 0x00 => {
                Data::try_from(buffer).map(|data| Self::Data(data.into()))
            }
            header if header & 0x60 == 0x00 => Ack::try_from(buffer).map(Self::Ack),
            header if header & 0x60 == 0x20 => Nak::try_from(buffer).map(Self::Nak),
            header => Err(io::Error::new(
                ErrorKind::InvalidData,
                format!("Unknown frame header: {header:#04X}"),
            )),
        }
    }
}

impl LowerHex for Frame {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Ack(ack) => LowerHex::fmt(ack, f),
            Self::Data(data) => LowerHex::fmt(data.as_ref(), f),
            Self::Error(error) => LowerHex::fmt(error, f),
            Self::Nak(nak) => LowerHex::fmt(nak, f),
            Self::Rst(rst) => LowerHex::fmt(rst, f),
            Self::RstAck(rst_ack) => LowerHex::fmt(rst_ack, f),
        }
    }
}

impl UpperHex for Frame {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Ack(ack) => UpperHex::fmt(ack, f),
            Self::Data(data) => UpperHex::fmt(data.as_ref(), f),
            Self::Error(error) => UpperHex::fmt(error, f),
            Self::Nak(nak) => UpperHex::fmt(nak, f),
            Self::Rst(rst) => UpperHex::fmt(rst, f),
            Self::RstAck(rst_ack) => UpperHex::fmt(rst_ack, f),
        }
    }
}

impl Validate for Frame {
    fn crc(&self) -> u16 {
        match self {
            Self::Ack(ack) => ack.crc(),
            Self::Data(data) => data.crc(),
            Self::Error(error) => error.crc(),
            Self::Nak(nak) => nak.crc(),
            Self::Rst(rst) => rst.crc(),
            Self::RstAck(rst_ack) => rst_ack.crc(),
        }
    }

    fn calculate_crc(&self) -> u16 {
        match self {
            Self::Ack(ack) => ack.calculate_crc(),
            Self::Data(data) => data.calculate_crc(),
            Self::Error(error) => error.calculate_crc(),
            Self::Nak(nak) => nak.calculate_crc(),
            Self::Rst(rst) => rst.calculate_crc(),
            Self::RstAck(rst_ack) => rst_ack.calculate_crc(),
        }
    }
}

#[cfg(test)]
#[expect(clippy::unwrap_used)]
mod tests {
    use super::{Frame, Rst};
    use crate::code::Code;
    use crate::validate::Validate;

    #[test]
    fn test_rst_try_from_bytes_slice() {
        let mut buffer = vec![0xC0, 0x38, 0xBC, 0x7E];
        let packet = Frame::try_from(buffer.drain(..buffer.len().saturating_sub(1))).unwrap();
        assert_eq!(packet, Frame::Rst(Rst::new()));
    }

    #[test]
    fn test_rstack_try_from_bytes_slice() {
        let mut buffer = vec![0xC1, 0x02, 0x02, 0x9B, 0x7B, 0x7E];

        match Frame::try_from(buffer.drain(..buffer.len().saturating_sub(1))).unwrap() {
            Frame::RstAck(rst_ack) => {
                assert!(rst_ack.is_ash_v2());
                assert_eq!(rst_ack.version(), 2);
                assert_eq!(rst_ack.code(), Ok(Code::PowerOn));
                assert_eq!(rst_ack.crc(), 0x9B7B);
            }
            packet => panic!("Expected RstAck, got {packet:?}"),
        }
    }

    #[test]
    fn test_error_try_from_bytes_slice() {
        let mut buffer = vec![0xC2, 0x02, 0x52, 0x98, 0xDE, 0x7E];

        match Frame::try_from(buffer.drain(..buffer.len().saturating_sub(1))).unwrap() {
            Frame::Error(error) => {
                assert_eq!(error.version(), 2);
                assert_eq!(error.code(), Err(0x52));
                assert_eq!(error.crc(), 0x98DE);
            }
            packet => panic!("Expected Error, got {packet:?}"),
        }
    }

    #[test]
    fn test_data_try_from_bytes_slice() {
        let buffer = vec![
            0x53, 0x00, 0x80, 0x00, 0x02, 0x02, 0x11, 0x30, 0x63, 0x16, 0x7E,
        ];

        match Frame::try_from(buffer.clone().drain(..buffer.len().saturating_sub(1))).unwrap() {
            Frame::Data(data) => {
                assert_eq!(data.crc(), 0x6316);
                let data = data.validate().unwrap();
                assert_eq!(data.into_payload().as_slice(), &buffer[1..buffer.len() - 3]);
            }
            packet => panic!("Expected Data, got {packet:?}"),
        }
    }

    #[test]
    fn test_ack_try_from_bytes_slice() {
        let mut buffer = vec![0x81, 0x60, 0x59, 0x7E];

        match Frame::try_from(buffer.drain(..buffer.len().saturating_sub(1))).unwrap() {
            Frame::Ack(ack) => {
                assert!(!ack.not_ready());
                assert_eq!(ack.ack_num().as_u8(), 1);
                assert_eq!(ack.crc(), 0x6059);
                assert!(ack.validate().is_ok());
            }
            packet => panic!("Expected Ack, got {packet:?}"),
        }

        let mut buffer = vec![0x8E, 0x91, 0xB6, 0x7E];

        match Frame::try_from(buffer.drain(..buffer.len().saturating_sub(1))).unwrap() {
            Frame::Ack(ack) => {
                assert!(ack.not_ready());
                assert_eq!(ack.ack_num().as_u8(), 0x06);
                assert_eq!(ack.crc(), 0x91B6);
                assert!(ack.validate().is_ok());
            }
            packet => panic!("Expected Ack, got {packet:?}"),
        }
    }

    #[test]
    fn test_nak_try_from_bytes_slice() {
        let mut buffer = vec![0xA6, 0x34, 0xDC, 0x7E];

        match Frame::try_from(buffer.drain(..buffer.len().saturating_sub(1))).unwrap() {
            Frame::Nak(nak) => {
                assert!(!nak.not_ready());
                assert_eq!(nak.ack_num().as_u8(), 0x06);
                assert_eq!(nak.crc(), 0x34DC);
                assert!(nak.validate().is_ok());
            }
            packet => panic!("Expected Nak, got {packet:?}"),
        }

        let mut buffer = vec![0xAD, 0x85, 0xB7, 0x7E];

        match Frame::try_from(buffer.drain(..buffer.len().saturating_sub(1))).unwrap() {
            Frame::Nak(nak) => {
                assert!(nak.not_ready());
                assert_eq!(nak.ack_num().as_u8(), 0x05);
                assert_eq!(nak.crc(), 0x85B7);
                assert!(nak.validate().is_ok());
            }
            packet => panic!("Expected Nak, got {packet:?}"),
        }
    }
}