use super::{
AsciiExt as _, ALERT_MARKER, COMMAND_MARKER, INFO_MARKER, MORE_PACKETS_MARKER, REPLY_MARKER,
};
use crate::ascii::response::{Flag, Kind, Status, Warning};
const CONT: &[u8] = b"cont";
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PacketKind {
Command,
Reply,
Info,
Alert,
}
impl PacketKind {
fn is_response(self) -> bool {
matches!(
self,
PacketKind::Reply | PacketKind::Info | PacketKind::Alert
)
}
}
impl From<Kind> for PacketKind {
fn from(other: Kind) -> Self {
match other {
Kind::Reply => PacketKind::Reply,
Kind::Info => PacketKind::Info,
Kind::Alert => PacketKind::Alert,
}
}
}
#[derive(Debug, Copy, Clone)]
enum ClientToken<'a> {
Reserved(&'a [u8]),
Word(&'a [u8]),
}
impl<'a> ClientToken<'a> {
fn into_bytes(self) -> &'a [u8] {
match self {
ClientToken::Reserved(bytes) | ClientToken::Word(bytes) => bytes,
}
}
}
pub(crate) trait Visitor<'a> {
type Output;
fn separator(&mut self, bytes: &'a [u8]);
fn kind(&mut self, kind: PacketKind, bytes: &'a [u8]);
fn device_address(&mut self, value: u8, bytes: &'a [u8]);
fn axis_number(&mut self, value: u8, bytes: &'a [u8]);
fn message_id(&mut self, value: u8, bytes: &'a [u8]);
fn flag(&mut self, flag: Flag, bytes: &'a [u8]);
fn status(&mut self, status: Status, bytes: &'a [u8]);
fn warning(&mut self, warning: Warning, bytes: &'a [u8]);
fn cont(&mut self, bytes: &'a [u8]);
fn cont_count(&mut self, value: u8, bytes: &'a [u8]);
fn data_word(&mut self, bytes: &'a [u8]);
fn data(&mut self, bytes: &'a [u8]);
fn hashed_content(&mut self, bytes: &'a [u8]);
fn more_packets_marker(&mut self, bytes: &'a [u8]);
fn checksum_marker(&mut self, bytes: &'a [u8]);
fn checksum(&mut self, value: u32, bytes: &'a [u8]);
fn termination(&mut self, bytes: &'a [u8]);
fn start_visit(&mut self, bytes: &'a [u8]);
fn finish_visit(self) -> Self::Output;
}
#[derive(Debug)]
pub(crate) struct Client<'a, V> {
visitor: V,
packet: &'a [u8],
index: usize,
}
impl<'a, V: Visitor<'a>> Client<'a, V> {
pub fn new(visitor: V, packet: &'a [u8]) -> Self {
Client {
visitor,
packet,
index: 0,
}
}
pub fn parse(mut self) -> Result<V::Output, ParseError> {
self.visitor.start_visit(self.packet);
let sep = self.count_separator();
if sep > 0 {
return Err(ParseError::MissingOrInvalidKind);
}
let kind = self.parse_kind()?;
let content_start_index = self.index;
self.parse_header(kind)?;
self.parse_body(kind)?;
let content_end_index = self.parse_footer()?;
let termination = self
.token_if(
|token| matches!(token, ClientToken::Reserved(bytes) if bytes.iter().all(u8::is_packet_end)),
)?
.map(ClientToken::into_bytes)
.ok_or(ParseError::MissingOrInvalidTermination)?;
self.visitor.termination(termination);
let content_end_index = content_end_index.unwrap_or(self.index - termination.len());
self.visitor
.hashed_content(&self.packet[content_start_index..content_end_index]);
if !self.rest().is_empty() {
return Err(ParseError::ExtraData);
}
Ok(self.visitor.finish_visit())
}
fn parse_kind(&mut self) -> Result<PacketKind, ParseError> {
use PacketKind as PK;
let kind;
if let Some(bytes) = self.reserved(&[COMMAND_MARKER])? {
kind = PK::Command;
self.visitor.kind(kind, bytes);
} else if let Some(bytes) = self.reserved(&[REPLY_MARKER])? {
kind = PK::Reply;
self.visitor.kind(kind, bytes);
} else if let Some(bytes) = self.reserved(&[INFO_MARKER])? {
kind = PK::Info;
self.visitor.kind(kind, bytes);
} else if let Some(bytes) = self.reserved(&[ALERT_MARKER])? {
kind = PK::Alert;
self.visitor.kind(kind, bytes);
} else {
return Err(ParseError::MissingOrInvalidKind);
}
Ok(kind)
}
fn parse_header(&mut self, kind: PacketKind) -> Result<(), ParseError> {
if let Some(digits) = self.digits()? {
let address = std::str::from_utf8(digits).unwrap().parse().unwrap();
self.visitor.device_address(address, digits);
if let Some(digits) = self.digits()? {
let axis = std::str::from_utf8(digits).unwrap().parse().unwrap();
self.visitor.axis_number(axis, digits);
if let Some(digits) = self.digits()? {
let id = std::str::from_utf8(digits).unwrap().parse().unwrap();
self.visitor.message_id(id, digits);
}
} else if kind.is_response() {
return Err(ParseError::MissingOrInvalidAxis);
}
} else if kind.is_response() {
return Err(ParseError::MissingOrInvalidAddress);
}
Ok(())
}
fn parse_body(&mut self, kind: PacketKind) -> Result<(), ParseError> {
match kind {
PacketKind::Command => {
if let Some(word) = self.word(CONT)? {
self.visitor.cont(word);
let word = self
.digits()?
.ok_or(ParseError::MissingOrInvalidContCount)?;
let count = std::str::from_utf8(word).unwrap().parse().unwrap();
self.visitor.cont_count(count, word);
}
self.parse_data()?;
}
PacketKind::Info => {
if let Some(word) = self.word(CONT)? {
self.visitor.cont(word);
}
self.parse_data()?;
}
PacketKind::Reply | PacketKind::Alert => {
if kind == PacketKind::Reply {
if let Some(word) = self.word(Flag::OK_STR.as_bytes())? {
self.visitor.flag(Flag::Ok, word);
} else if let Some(word) = self.word(Flag::RJ_STR.as_bytes())? {
self.visitor.flag(Flag::Rj, word);
} else {
return Err(ParseError::MissingOrInvalidFlag);
}
}
if let Some(word) = self.word(Status::IDLE_STR.as_bytes())? {
self.visitor.status(Status::Idle, word);
} else if let Some(word) = self.word(Status::BUSY_STR.as_bytes())? {
self.visitor.status(Status::Busy, word);
} else {
return Err(ParseError::MissingOrInvalidStatus);
}
let word = self
.any_word()?
.ok_or(ParseError::MissingOrInvalidWarning)?;
let warning = Warning::from(
TryInto::<[u8; 2]>::try_into(word)
.map_err(|_| ParseError::MissingOrInvalidWarning)?,
);
self.visitor.warning(warning, word);
self.parse_data()?;
}
}
Ok(())
}
fn parse_footer(&mut self) -> Result<Option<usize>, ParseError> {
let mut content_end_index = None;
if let Some(word) = self.reserved(&[MORE_PACKETS_MARKER])? {
self.visitor.more_packets_marker(word);
}
if let Some(word) = self.reserved(b":")? {
content_end_index = Some(self.index - 1);
self.visitor.checksum_marker(word);
let word = self
.hex_digits()?
.ok_or(ParseError::MissingOrInvalidChecksum)?;
let checksum = std::str::from_utf8(word).unwrap();
let checksum = u32::from_str_radix(checksum, 16)
.map_err(|_| ParseError::MissingOrInvalidChecksum)?;
self.visitor.checksum(checksum, word);
}
Ok(content_end_index)
}
fn token_if<F>(&mut self, mut predicate: F) -> Result<Option<ClientToken<'a>>, ParseError>
where
F: FnMut(ClientToken<'a>) -> bool,
{
if self.index == self.packet.len() {
return Ok(None);
}
let ws_count = self.count_separator();
let start = self.index + ws_count;
let count = self.packet[start..]
.iter()
.take_while(|c| c.is_packet_end())
.count();
if count > 0 {
let token = ClientToken::Reserved(&self.packet[start..start + count]);
if (predicate)(token) {
self.separator();
self.index += count;
return Ok(Some(token));
}
return Ok(None);
}
if self.packet[start].is_reserved() {
let token = ClientToken::Reserved(&self.packet[start..=start]);
if (predicate)(token) {
self.separator();
self.index += 1;
return Ok(Some(token));
}
return Ok(None);
}
let count = self.packet[start..]
.iter()
.take_while(|c| !c.is_separator() && !c.is_reserved())
.count();
assert!(count > 0);
let valid_ascii = self.packet[start..start + count].iter().all(u8::is_ascii);
if !valid_ascii {
return Err(ParseError::NonAscii);
}
let token = ClientToken::Word(&self.packet[start..start + count]);
if (predicate)(token) {
self.separator();
self.index += count;
Ok(Some(token))
} else {
Ok(None)
}
}
fn count_separator(&self) -> usize {
self.packet[self.index..]
.iter()
.take_while(|c| c.is_separator())
.count()
}
fn separator(&mut self) -> Option<&'a [u8]> {
let count = self.count_separator();
if count > 0 {
let slice = &self.packet[self.index..self.index + count];
self.index += count;
self.visitor.separator(slice);
Some(slice)
} else {
None
}
}
fn digits(&mut self) -> Result<Option<&'a [u8]>, ParseError> {
Ok(self
.token_if(
|token| matches!(token, ClientToken::Word(bytes) if bytes.iter().all(u8::is_ascii_digit)),
)?
.map(ClientToken::into_bytes))
}
fn hex_digits(&mut self) -> Result<Option<&'a [u8]>, ParseError> {
Ok(self
.token_if(
|token| matches!(token, ClientToken::Word(bytes) if bytes.iter().all(u8::is_ascii_hexdigit)),
)?
.map(ClientToken::into_bytes))
}
fn any_word(&mut self) -> Result<Option<&'a [u8]>, ParseError> {
Ok(self
.token_if(|token| matches!(token, ClientToken::Word(_)))?
.map(ClientToken::into_bytes))
}
fn word(&mut self, tag: &[u8]) -> Result<Option<&'a [u8]>, ParseError> {
Ok(self
.token_if(|token| matches!(token, ClientToken::Word(bytes) if bytes == tag))?
.map(ClientToken::into_bytes))
}
fn reserved(&mut self, tag: &[u8]) -> Result<Option<&'a [u8]>, ParseError> {
Ok(self
.token_if(|token| matches!(token, ClientToken::Reserved(bytes) if bytes == tag))?
.map(ClientToken::into_bytes))
}
fn parse_data(&mut self) -> Result<(), ParseError> {
self.separator();
let start = self.index;
while let Some(word) = self.any_word()? {
self.visitor.data_word(word);
}
if self.index > start {
self.visitor.data(&self.packet[start..self.index]);
}
Ok(())
}
fn rest(&self) -> &'a [u8] {
&self.packet[self.index..]
}
}
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) enum ParseError {
MissingOrInvalidKind,
MissingOrInvalidAddress,
MissingOrInvalidAxis,
MissingOrInvalidContCount,
MissingOrInvalidFlag,
MissingOrInvalidStatus,
MissingOrInvalidWarning,
MissingOrInvalidChecksum,
MissingOrInvalidTermination,
NonAscii,
ExtraData,
}