use bytes::{BufMut, Bytes, BytesMut};
#[cfg(feature = "debug")]
use thiserror::Error;
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug, Error))]
pub enum ParseError {
#[cfg_attr(feature = "debug", error("packet was empty"))]
EmptyPacket,
#[cfg_attr(feature = "debug", error("expected length: {expected}, got: {actual}"))]
UnexpectedLength {
expected: usize,
actual: usize,
data: Bytes,
},
}
pub fn frame<T: AsRef<[u8]>>(packet: T) -> Bytes {
let mut buf = BytesMut::with_capacity(65);
buf.put_u8((packet.as_ref().len() + 1) as u8);
buf.extend_from_slice(packet.as_ref());
buf.put_u8(checksum(&buf));
buf.freeze()
}
pub fn checksum<T: AsRef<[u8]>>(data: T) -> u8 {
(data.as_ref().iter().map(|&x| x as u32).sum::<u32>() & 0xFF) as u8
}
pub fn unframe(response: Bytes) -> Result<Bytes, ParseError> {
if response.is_empty() {
return Err(ParseError::EmptyPacket);
}
let len = response[0] as usize;
if response.len() < len {
Err(ParseError::UnexpectedLength {
expected: len,
actual: response.len(),
data: response,
})
} else if len == 0 {
Err(ParseError::EmptyPacket)
} else {
Ok(response.slice(1..len))
}
}
#[cfg(test)]
mod test {
use alloc::vec;
use super::*;
#[test]
fn frame_test() {
let packet = Bytes::from_static(&[0x05, 0xFF, 0xDA, 0x02]);
let framed = frame(packet.clone());
assert_eq!(
framed.len(),
packet.len() + 2,
"length should be len(data) + 1 (len) + 1 (checksum)"
);
assert_eq!(
framed[0], 5,
"the first byte should indicate the length of the packet including the checksum byte"
);
assert!(
framed[1..5].iter().eq(packet.iter()),
"the packet data should be there verbatim"
);
assert_eq!(framed[5], 229, "The checksum should be accurate");
}
#[test]
fn unframe_test() {
let response = Bytes::from_static(&[0x3, 0x1, 0x2, 0xFF, 0xFF, 0xFF]);
let frame = unframe(response).ok().unwrap();
assert_eq!(
frame,
vec![0x1, 0x2],
"should remove the length header and return the right data"
);
}
}