use crate::ascii::{
packet::visitor::{Client, PacketKind, Visitor},
response::{Flag, Status, Warning},
};
use crate::error::AsciiPacketMalformedError;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Token {
Kind,
DeviceAddress,
AxisNumber,
MessageId,
Flag,
Status,
Warning,
Cont,
ContCount,
DataWord,
MorePacketsMarker,
ChecksumMarker,
Checksum,
Terminator,
Separator,
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub struct Tokens<T = Box<[u8]>> {
pub(crate) packet: T,
pub(crate) items: Vec<(Token, u8)>,
}
pub type RefTokens<'a> = Tokens<&'a [u8]>;
impl Tokens<Box<[u8]>> {
pub fn new(packet: &[u8]) -> Result<Self, AsciiPacketMalformedError> {
Tokens::try_from(packet)
}
}
impl<'a> Tokens<&'a [u8]> {
pub fn new_ref(packet: &'a [u8]) -> Result<Self, AsciiPacketMalformedError> {
Tokens::try_from(packet)
}
}
impl<T> Tokens<T>
where
T: Default,
{
pub(crate) fn new_default() -> Self {
Tokens {
packet: T::default(),
items: Vec::new(),
}
}
}
impl<T> Tokens<T>
where
T: AsRef<[u8]>,
{
pub fn iter(&self) -> TokenIter<'_, T> {
TokenIter {
tokens: self,
token_index: 0,
packet_index: 0,
}
}
pub fn as_str(&self) -> &str {
std::str::from_utf8(self.packet.as_ref()).unwrap()
}
}
impl<'a, T> Visitor<'a> for Tokens<T>
where
T: From<&'a [u8]>,
{
#![allow(clippy::cast_possible_truncation)]
type Output = Self;
fn separator(&mut self, bytes: &[u8]) {
self.items.push((Token::Separator, bytes.len() as u8));
}
fn kind(&mut self, _: PacketKind, bytes: &[u8]) {
self.items.push((Token::Kind, bytes.len() as u8));
}
fn device_address(&mut self, _: u8, bytes: &[u8]) {
self.items.push((Token::DeviceAddress, bytes.len() as u8));
}
fn axis_number(&mut self, _: u8, bytes: &[u8]) {
self.items.push((Token::AxisNumber, bytes.len() as u8));
}
fn message_id(&mut self, _: u8, bytes: &[u8]) {
self.items.push((Token::MessageId, bytes.len() as u8));
}
fn flag(&mut self, _: Flag, bytes: &[u8]) {
self.items.push((Token::Flag, bytes.len() as u8));
}
fn status(&mut self, _: Status, bytes: &[u8]) {
self.items.push((Token::Status, bytes.len() as u8));
}
fn warning(&mut self, _: Warning, bytes: &[u8]) {
self.items.push((Token::Warning, bytes.len() as u8));
}
fn cont(&mut self, bytes: &[u8]) {
self.items.push((Token::Cont, bytes.len() as u8));
}
fn cont_count(&mut self, _: u8, bytes: &[u8]) {
self.items.push((Token::ContCount, bytes.len() as u8));
}
fn data_word(&mut self, bytes: &[u8]) {
self.items.push((Token::DataWord, bytes.len() as u8));
}
fn data(&mut self, _: &'a [u8]) {
}
fn hashed_content(&mut self, _: &'a [u8]) {
}
fn more_packets_marker(&mut self, bytes: &[u8]) {
self.items
.push((Token::MorePacketsMarker, bytes.len() as u8));
}
fn checksum_marker(&mut self, bytes: &[u8]) {
self.items.push((Token::ChecksumMarker, bytes.len() as u8));
}
fn checksum(&mut self, _: u32, bytes: &[u8]) {
self.items.push((Token::Checksum, bytes.len() as u8));
}
fn termination(&mut self, bytes: &[u8]) {
self.items.push((Token::Terminator, bytes.len() as u8));
}
fn start_visit(&mut self, bytes: &'a [u8]) {
self.packet = bytes.into();
}
fn finish_visit(self) -> Self::Output {
self
}
}
impl<'a> TryFrom<&'a [u8]> for Tokens<Box<[u8]>> {
type Error = AsciiPacketMalformedError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
let client = Client::new(Tokens::new_default(), value);
client
.parse()
.map_err(|_| AsciiPacketMalformedError::new(value))
}
}
impl<'a> TryFrom<&'a [u8]> for Tokens<&'a [u8]> {
type Error = AsciiPacketMalformedError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
let client = Client::new(Tokens::new_default(), value);
client
.parse()
.map_err(|_| AsciiPacketMalformedError::new(value))
}
}
impl<'a, T> IntoIterator for &'a Tokens<T>
where
T: AsRef<[u8]>,
{
type IntoIter = TokenIter<'a, T>;
type Item = (Token, &'a str);
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Debug)]
pub struct TokenIter<'a, T> {
tokens: &'a Tokens<T>,
token_index: u8,
packet_index: u8,
}
impl<'a, T> Iterator for TokenIter<'a, T>
where
T: AsRef<[u8]>,
{
type Item = (Token, &'a str);
fn next(&mut self) -> Option<Self::Item> {
if let Some((token, len)) = self.tokens.items.get(self.token_index as usize) {
let start = self.packet_index as usize;
self.packet_index += *len;
self.token_index += 1;
Some((
*token,
std::str::from_utf8(&self.tokens.packet.as_ref()[start..start + (*len as usize)])
.unwrap(),
))
} else {
None
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn type_alias() {
let _: Result<RefTokens<'_>, AsciiPacketMalformedError> = Tokens::new_ref(b"");
}
#[test]
fn iter() {
struct Case {
tokens: Tokens<&'static [u8]>,
expected: &'static [(Token, &'static str)],
}
let cases = &[Case {
tokens: Tokens::new_ref(b"@01 2 3 OK BUSY WR 0.123 1.234 NA\\:55\r\n").unwrap(),
expected: &[
(Token::Kind, "@"),
(Token::DeviceAddress, "01"),
(Token::Separator, " "),
(Token::AxisNumber, "2"),
(Token::Separator, " "),
(Token::MessageId, "3"),
(Token::Separator, " "),
(Token::Flag, "OK"),
(Token::Separator, " "),
(Token::Status, "BUSY"),
(Token::Separator, " "),
(Token::Warning, "WR"),
(Token::Separator, " "),
(Token::DataWord, "0.123"),
(Token::Separator, " "),
(Token::DataWord, "1.234"),
(Token::Separator, " "),
(Token::DataWord, "NA"),
(Token::MorePacketsMarker, "\\"),
(Token::ChecksumMarker, ":"),
(Token::Checksum, "55"),
(Token::Terminator, "\r\n"),
],
}];
for case in cases {
assert_eq!(case.tokens.iter().collect::<Vec<_>>(), case.expected);
}
}
}