use crate::ascii::{
command::Target,
packet::visitor::{Client, PacketKind, Visitor},
response::{Flag, Status, Warning},
};
use crate::error::AsciiPacketMalformedError;
#[derive(Debug, Clone, PartialEq)]
pub(super) struct InnerPacket<T> {
pub packet: T,
pub kind: PacketKind,
pub target: (u8, u8),
pub id: Option<u8>,
pub flag: Option<Flag>,
pub status: Option<Status>,
pub warning: Option<Warning>,
pub data: T,
pub hashed_content: T,
pub cont: bool,
pub cont_count: Option<u8>,
pub more_packets: bool,
pub checksum: Option<u32>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Packet<T = Box<[u8]>>(Box<InnerPacket<T>>);
impl<T> From<InnerPacket<T>> for Packet<T> {
fn from(other: InnerPacket<T>) -> Self {
Packet(Box::new(other))
}
}
pub type RefPacket<'a> = Packet<&'a [u8]>;
impl Packet<Box<[u8]>> {
pub fn new(packet: &[u8]) -> Result<Self, AsciiPacketMalformedError> {
Packet::try_from(packet)
}
}
impl<'a> Packet<&'a [u8]> {
pub fn new_ref(packet: &'a [u8]) -> Result<Self, AsciiPacketMalformedError> {
Packet::try_from(packet)
}
}
impl<T> Packet<T>
where
T: Default,
{
pub(super) fn new_default() -> Self {
Packet::from(InnerPacket {
packet: Default::default(),
kind: PacketKind::Command,
target: (0, 0),
id: None,
flag: None,
status: None,
warning: None,
data: Default::default(),
hashed_content: Default::default(),
cont: false,
cont_count: None,
more_packets: false,
checksum: None,
})
}
}
impl<T> Packet<T>
where
T: AsRef<[u8]>,
{
pub fn as_bytes(&self) -> &[u8] {
self.0.packet.as_ref()
}
pub fn as_str(&self) -> &str {
std::str::from_utf8(self.as_bytes()).unwrap()
}
pub fn kind(&self) -> PacketKind {
self.0.kind
}
pub fn target(&self) -> Target {
self.0.target.into()
}
pub fn id(&self) -> Option<u8> {
self.0.id
}
pub fn cont(&self) -> bool {
self.0.cont
}
pub fn cont_count(&self) -> Option<u8> {
self.0.cont_count
}
pub fn flag(&self) -> Option<Flag> {
self.0.flag
}
pub fn status(&self) -> Option<Status> {
self.0.status
}
pub fn warning(&self) -> Option<Warning> {
self.0.warning
}
pub fn data(&self) -> &str {
std::str::from_utf8(self.0.data.as_ref()).unwrap()
}
pub(crate) fn hashed_content(&self) -> &[u8] {
self.0.hashed_content.as_ref()
}
pub fn more_packets(&self) -> bool {
self.0.more_packets
}
pub fn checksum(&self) -> Option<u32> {
self.0.checksum
}
}
impl<T> std::fmt::Display for Packet<T>
where
T: AsRef<[u8]>,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl<'a, T> Visitor<'a> for Packet<T>
where
T: From<&'a [u8]>,
{
type Output = Self;
fn separator(&mut self, _: &[u8]) {}
fn kind(&mut self, kind: PacketKind, _: &[u8]) {
self.0.kind = kind;
}
fn device_address(&mut self, address: u8, _: &[u8]) {
self.0.target.0 = address;
}
fn axis_number(&mut self, axis: u8, _: &[u8]) {
self.0.target.1 = axis;
}
fn message_id(&mut self, id: u8, _: &[u8]) {
self.0.id = Some(id);
}
fn flag(&mut self, flag: Flag, _: &[u8]) {
self.0.flag = Some(flag);
}
fn status(&mut self, status: Status, _: &[u8]) {
self.0.status = Some(status);
}
fn warning(&mut self, warning: Warning, _: &[u8]) {
self.0.warning = Some(warning);
}
fn cont(&mut self, _: &[u8]) {
self.0.cont = true;
}
fn cont_count(&mut self, count: u8, _: &[u8]) {
self.0.cont_count = Some(count);
}
fn data_word(&mut self, _: &'a [u8]) {}
fn data(&mut self, bytes: &'a [u8]) {
self.0.data = bytes.into();
}
fn hashed_content(&mut self, bytes: &'a [u8]) {
self.0.hashed_content = bytes.into();
}
fn more_packets_marker(&mut self, _: &[u8]) {
self.0.more_packets = true;
}
fn checksum_marker(&mut self, _: &[u8]) {}
fn checksum(&mut self, checksum: u32, _: &[u8]) {
self.0.checksum = Some(checksum);
}
fn termination(&mut self, _: &[u8]) {}
fn start_visit(&mut self, bytes: &'a [u8]) {
self.0.packet = bytes.into();
}
fn finish_visit(self) -> Self::Output {
self
}
}
impl<'a> TryFrom<&'a [u8]> for Packet<Box<[u8]>> {
type Error = AsciiPacketMalformedError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
let client = Client::new(Packet::new_default(), value);
client
.parse()
.map_err(|_| AsciiPacketMalformedError::new(value))
}
}
impl<'a> TryFrom<&'a [u8]> for Packet<&'a [u8]> {
type Error = AsciiPacketMalformedError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
let client = Client::new(Packet::new_default(), value);
client
.parse()
.map_err(|_| AsciiPacketMalformedError::new(value))
}
}
#[cfg(test)]
mod type_test {
use super::*;
#[test]
fn type_alias() {
let _: Result<RefPacket<'_>, AsciiPacketMalformedError> = Packet::new_ref(b"");
}
}