mod accumulator;
mod deserializer;
pub use self::accumulator::{Accumulator, FeedResult};
use self::deserializer::Deserializer;
use crate::{
command::{Command, Curve, Dword, Read, Register, Word, Write},
Error::*,
Result, CRC, HEADER,
};
use serde::Deserialize;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Content<'de> {
deserializer: Deserializer<'de>,
}
impl<'de> Content<'de> {
pub fn take<T: Deserialize<'de>>(&mut self) -> Result<T> {
T::deserialize(&mut self.deserializer)
}
pub fn len(&self) -> usize {
self.deserializer.input.len()
}
pub fn is_empty(&self) -> bool {
self.deserializer.input.is_empty()
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Response<'de> {
RegisterAck,
WordAck,
DwordAck,
CurveAck,
RegisterData {
cmd: Register<Read>,
content: Content<'de>,
},
WordData {
cmd: Word<Read>,
content: Content<'de>,
},
DwordData {
cmd: Dword<Read>,
content: Content<'de>,
},
}
impl<'de> Response<'de> {
pub fn from_bytes(input: &'de [u8], crc: bool) -> Result<Self> {
let (response, _) = Self::take_from_bytes(input, crc)?;
Ok(response)
}
pub fn take_from_bytes(input: &'de [u8], crc: bool) -> Result<(Self, &'de [u8])> {
let (input, rest) = Self::extract_content_bytes(input, crc)?;
Ok((Self::from_content_bytes(input)?, rest))
}
pub fn from_content_bytes(input: &'de [u8]) -> Result<Self> {
let mut deserializer = Deserializer { input };
let opcode = u8::deserialize(&mut deserializer)?;
use Response::*;
if opcode % 2 == 0 {
let response = match opcode {
Register::<Write>::CMD => RegisterAck,
Word::<Write>::CMD => WordAck,
Dword::<Write>::CMD => DwordAck,
Curve::CMD => CurveAck,
_ => return Err(ResponseUnknownCmd),
};
if u16::deserialize(&mut deserializer)? != u16::from_be_bytes([b'O', b'K']) {
return Err(ResponseBadAck);
}
Ok(response)
}
else {
let response = match opcode {
Register::<Read>::CMD => RegisterData {
cmd: Register::deserialize(&mut deserializer)?,
content: Content { deserializer },
},
Word::<Read>::CMD => WordData {
cmd: Word::deserialize(&mut deserializer)?,
content: Content { deserializer },
},
Dword::<Read>::CMD => DwordData {
cmd: Dword::deserialize(&mut deserializer)?,
content: Content { deserializer },
},
_ => return Err(ResponseUnknownCmd),
};
Ok(response)
}
}
fn extract_content_bytes(input: &'de [u8], crc: bool) -> Result<(&'de [u8], &'de [u8])> {
let input = input
.strip_prefix(&u16::to_be_bytes(HEADER))
.ok_or(ResponseBadHeader)?;
let (len, input) = input.split_first().ok_or(DeserializeUnexpectedEnd)?;
let len = *len as usize;
let min_len = if crc { 5 } else { 3 };
if len < min_len {
return Err(ResponseBadLen);
}
let (input, rest) = input.split_at_checked(len).ok_or(ResponseTooLarge)?;
let input = if crc {
let (input, crc) = input.split_last_chunk().ok_or(DeserializeUnexpectedEnd)?;
if u16::from_le_bytes(*crc) != CRC.checksum(input) {
return Err(ResponseBadCrc);
}
input
} else {
input
};
Ok((input, rest))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ack_crc() {
let input = [0x5A, 0xA5, 5, 0x82, b'O', b'K', 0xA5, 0xEF, 1, 2, 3, 4];
let (response, rest) = Response::take_from_bytes(&input, true).unwrap();
let Response::WordAck = response else {
panic!()
};
assert_eq!(rest, &[1, 2, 3, 4]);
}
}