#[macro_export]
macro_rules! packet {
(
struct $name:ident {
$(
$field:ident: $ty:ty
),* $(,)?
}
) => {
#[derive(Debug)]
#[allow(non_snake_case)]
pub struct $name {
$($field: $ty),*
}
/// Trait fitting implementations
impl $crate::PacketContent for $name {}
impl $crate::Listable for $name {}
impl $crate::Codec for $name {
#[allow(non_snake_case)]
fn encode(&self, output: &mut Vec<u8>) {
$(
$crate::Tag::encode_from(stringify!($field), &(<$ty>::value_type()), output);
<$ty>::encode(&self.$field, output);
)*
}
#[allow(non_snake_case)]
fn decode(reader: &mut $crate::Reader) -> $crate::CodecResult<Self> {
$(
$crate::Tag::expect_tag(stringify!($field), &(<$ty>::value_type()), reader)?;
let $field = <$ty>::decode(reader)
.map_err(|err|$crate::CodecError::DecodeFail(stringify!($field), Box::new(err)))?;
)*
Ok(Self {
$($field),*
})
}
}
};
}
#[macro_export]
macro_rules! group {
(
struct $name:ident {
$(
$field:ident: $ty:ty
),* $(,)?
}
) => {
#[derive(Debug)]
#[allow(non_snake_case)]
pub struct $name {
$($field: $ty),*
}
impl $crate::Listable for $name {}
impl $crate::Codec for $name {
#[allow(non_snake_case)]
fn encode(&self, output: &mut Vec<u8>) {
$(
$crate::Tag::encode_from(stringify!($field), &(<$ty>::value_type()), output);
<$ty>::encode(&self.$field, output);
)*
output.push(0)
}
#[allow(non_snake_case)]
fn decode(reader: &mut $crate::Reader) -> $crate::CodecResult<Self> {
$crate::Tag::take_two(reader)?;
$(
$crate::Tag::expect_tag(stringify!($field), &(<$ty>::value_type()), reader)?;
let $field = <$ty>::decode(reader)
.map_err(|err|$crate::CodecError::DecodeFail(stringify!($field), Box::new(err)))?;
)*
$crate::Tag::discard_group(reader)?;
Ok(Self {
$($field),*
})
}
fn value_type() -> $crate::ValueType {
$crate::ValueType::Group
}
}
};
(
(2) struct $name:ident {
$(
$field:ident: $ty:ty
),* $(,)?
}
) => {
#[derive(Debug)]
#[allow(non_snake_case)]
pub struct $name {
$($field: $ty),*
}
impl $crate::Listable for $name {}
impl $crate::Codec for $name {
#[allow(non_snake_case)]
fn encode(&self, output: &mut Vec<u8>) {
output.push(2);
$(
$crate::Tag::encode_from(stringify!($field), &(<$ty>::value_type()), output);
<$ty>::encode(&self.$field, output);
)*
output.push(0);
}
#[allow(non_snake_case)]
fn decode(reader: &mut $crate::Reader) -> $crate::CodecResult<Self> {
$crate::Tag::take_two(reader)?;
$(
$crate::Tag::expect_tag(stringify!($field), &(<$ty>::value_type()), reader)?;
let $field = <$ty>::decode(reader)
.map_err(|err|$crate::CodecError::DecodeFail(stringify!($field), Box::new(err)))?;
)*
$crate::Tag::discard_group(reader)?;
Ok(Self {
$($field),*
})
}
fn value_type() -> $crate::ValueType {
$crate::ValueType::Group
}
}
};
}
#[macro_export]
macro_rules! define_components {
(
$(
$component:ident ($component_value:literal) {
$(
$command:ident ($command_value:literal)
)*
}
)*
) => {
pub mod components {
$(
#[derive(Debug, Eq, PartialEq)]
pub enum $component {
$($command),*,
Unknown(u16)
}
impl $crate::PacketComponent for $component {
fn component(&self) -> u16 {
$component_value
}
fn command(&self) -> u16 {
match self {
$(Self::$command => $command_value),*,
Self::Unknown(value) => *value,
}
}
fn from_value(value: u16) -> Self {
match value {
$($command_value => Self::$command),*,
value => Self::Unknown(value)
}
}
}
)*
}
};
}
#[cfg(test)]
mod test {
use crate::{Codec, Reader};
use crate::{TdfMap, TdfOptional, VarInt, VarIntList};
packet! {
struct TestStruct {
AA: u8,
AB: VarInt,
AC: String,
AD: Vec<u8>,
AE: MyGroup,
AF: Vec<String>,
AG: Vec<MyGroup>,
AH: TdfMap<String, String>,
AI: TdfOptional<String>,
AK: VarIntList,
AL: (VarInt, VarInt),
AM: (VarInt, VarInt, VarInt)
}
}
group! {
struct MyGroup {
ABCD: String
}
}
#[test]
fn test() {
let mut map = TdfMap::<String, String>::new();
map.insert("Test", "Map");
map.insert("Other", "Test");
map.insert("New", "Value");
let str = TestStruct {
AA: 254,
AB: VarInt(12),
AC: String::from("test"),
AD: vec![0, 5, 12, 5],
AE: MyGroup {
ABCD: String::from("YES"),
},
AF: vec![String::from("ABC"), String::from("Abced")],
AG: vec![
MyGroup {
ABCD: String::from("YES1"),
},
MyGroup {
ABCD: String::from("YES2"),
},
],
AH: map,
AI: TdfOptional::<String>::None,
AK: VarIntList(vec![VarInt(1)]),
AL: (VarInt(5), VarInt(256)),
AM: (VarInt(255), VarInt(6000), VarInt(6743)),
};
let out = str.encode_bytes();
println!("{out:?}");
let mut reader = Reader::new(&out);
let str_out = TestStruct::decode(&mut reader).unwrap();
println!("{str_out:?}");
assert_eq!(str.AB, str_out.AB);
assert_eq!(str.AC, str_out.AC);
assert_eq!(str.AD, str_out.AD);
assert_eq!(str.AB, str_out.AB);
assert_eq!(str.AB, str_out.AB);
}
}