use std::fmt::{self, Display, Formatter};
const DLE: u8 = 0x10;
const STX: u8 = 0x02;
const ETX: u8 = 0x03;
const NAK: u8 = 0x15;
const HEADER: [u8; 2] = [DLE, STX];
const FOOTER: [u8; 2] = [DLE, ETX];
const NEGATIVE_ACKNOWLEDGEMENT: [u8; 2] = [DLE, NAK];
const HEADER_LENGTH: usize = HEADER.len();
const FOOTER_LENGTH: usize = FOOTER.len();
const BCC_LENGTH: usize = 1;
const LENGTH_MIN: usize = HEADER_LENGTH + FOOTER_LENGTH + BCC_LENGTH;
pub(crate) fn parse_command(command: &[u8]) -> Vec<u8> {
let mut buf = HEADER.to_vec();
let mut bcc = 0;
for byte in command.iter().copied() {
if byte == DLE {
buf.push(byte);
}
buf.push(byte);
bcc ^= byte;
}
for byte in FOOTER.iter().copied() {
buf.push(byte);
if byte != DLE {
bcc ^= byte;
}
}
buf.push(bcc);
buf
}
pub(crate) fn parse_reception(message: &[u8]) -> Result<Vec<u8>, ParseError> {
if message == &NEGATIVE_ACKNOWLEDGEMENT[..] {
return Err(ParseError::NAK);
}
let len = message.len();
if len < LENGTH_MIN {
return Err(ParseError::TooShort(len));
}
let mut buf = vec![];
let mut is_previous_dle = false;
let mut expected_bcc = 0;
for &byte in &message[HEADER_LENGTH..message.len() - FOOTER_LENGTH - BCC_LENGTH] {
if is_previous_dle {
if byte != DLE {
return Err(ParseError::WrongDLE);
}
is_previous_dle = false;
} else {
buf.push(byte);
expected_bcc ^= byte;
is_previous_dle = byte == DLE;
}
}
FOOTER
.iter()
.filter(|&&byte| byte != DLE)
.for_each(|&byte| expected_bcc ^= byte);
let actual_bcc = *message.last().unwrap();
if expected_bcc != actual_bcc {
return Err(ParseError::WrongBCC);
}
Ok(buf)
}
#[derive(Debug)]
pub enum ParseError {
NAK,
TooShort(usize),
WrongDLE,
WrongBCC,
}
impl Display for ParseError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
ParseError::NAK => write!(f, "Received an negative acknowledgement."),
ParseError::TooShort(l) => write!(f, "Received too short ({} bytes) message,", l),
ParseError::WrongDLE => write!(
f,
"DLE must be contained twice or more in a row, but was not."
),
ParseError::WrongBCC => write!(f, "Wrong BCC."),
}
}
}
impl std::error::Error for ParseError {}