756 lines
26 KiB
Rust
756 lines
26 KiB
Rust
// TODO: try less hard, just mem::transmute all this junk away
|
|
// TODO: PSOPacketData derive
|
|
|
|
#![recursion_limit="256"]
|
|
|
|
extern crate proc_macro;
|
|
|
|
use proc_macro::TokenStream;
|
|
use syn::{parse_macro_input, ItemStruct, DeriveInput, Field};
|
|
use syn::punctuated::Iter;
|
|
use syn::punctuated::Punctuated;
|
|
use syn::parse::Parse;
|
|
use quote::quote;
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
enum AttrMeta {
|
|
None,
|
|
Utf8,
|
|
Utf16,
|
|
NoDebug,
|
|
LengthIs(syn::Ident),
|
|
LengthOf(syn::Ident),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum AttrType {
|
|
Value(syn::TypePath, syn::Ident, AttrMeta),
|
|
Array(syn::TypePath, syn::Ident, usize, AttrMeta)
|
|
}
|
|
|
|
fn generate_struct_def(name: syn::Ident, attrs: &Vec<AttrType>) -> proc_macro2::TokenStream {
|
|
let mut struct_def = Vec::new();
|
|
for attr in attrs {
|
|
let element = match attr {
|
|
AttrType::Value(ty, name, _) => {
|
|
quote!(pub #name: #ty)
|
|
},
|
|
AttrType::Array(ty, name, len, _) => {
|
|
quote!(pub #name: [#ty; #len])
|
|
}
|
|
};
|
|
struct_def.push(element);
|
|
}
|
|
|
|
quote! {
|
|
pub struct #name {
|
|
#(#struct_def),*
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
fn generate_from_bytes(attrs: &Vec<AttrType>) -> Vec<proc_macro2::TokenStream> {
|
|
let mut from_bytes = Vec::new();
|
|
for attr in attrs {
|
|
let element = match attr {
|
|
AttrType::Value(ty, name, _) => {
|
|
let type_str = ty.path.segments[0].ident.to_string();
|
|
if type_str == "Vec" {
|
|
let vec_type = match &ty.path.segments[0].arguments {
|
|
syn::PathArguments::AngleBracketed(arg) => {
|
|
match &arg.args[0] {
|
|
syn::GenericArgument::Type(typ) => {
|
|
match &typ {
|
|
syn::Type::Path(path) => {
|
|
Some(path.path.segments[0].ident.clone())
|
|
}
|
|
_ => None
|
|
}
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
_ => None
|
|
}.unwrap();
|
|
|
|
quote! {
|
|
#name: {
|
|
let mut tmp = Vec::new();
|
|
|
|
for _ in 0..flag {
|
|
tmp.push(<#vec_type as PSOPacketData>::from_bytes(&mut cur)?);
|
|
}
|
|
tmp
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
quote! {
|
|
#name: <#ty as PSOPacketData>::from_bytes(&mut cur)?,
|
|
}
|
|
}
|
|
},
|
|
AttrType::Array(ty, name, len, _) => {
|
|
quote! {
|
|
#name: {
|
|
let mut arr = [#ty::default(); #len];
|
|
for e in arr.iter_mut() {
|
|
*e = #ty::from_bytes(&mut cur)?
|
|
}
|
|
arr
|
|
},
|
|
}
|
|
}
|
|
};
|
|
from_bytes.push(element);
|
|
}
|
|
from_bytes
|
|
}
|
|
|
|
fn generate_as_bytes(attrs: &Vec<AttrType>) -> Vec<proc_macro2::TokenStream> {
|
|
let mut as_bytes = Vec::new();
|
|
for attr in attrs {
|
|
let element = match attr {
|
|
AttrType::Value(ty, name, _) => {
|
|
let type_str = ty.path.segments[0].ident.to_string();
|
|
if type_str == "Vec" {
|
|
quote! {
|
|
flag = self.#name.len() as u32;
|
|
for i in self.#name.iter() {
|
|
buf.extend_from_slice(&PSOPacketData::as_bytes(i));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
quote! {
|
|
buf.extend_from_slice(&PSOPacketData::as_bytes(&self.#name));
|
|
}
|
|
}
|
|
},
|
|
AttrType::Array(_ty, name, len, _) => {
|
|
quote! {
|
|
for i in 0..#len {
|
|
buf.extend_from_slice(&self.#name[i].as_bytes());
|
|
}
|
|
}
|
|
}
|
|
};
|
|
as_bytes.push(element);
|
|
}
|
|
as_bytes
|
|
}
|
|
|
|
|
|
fn generate_psopacket_impl(pkt_cmd: u16, name: syn::Ident, attrs: &Vec<AttrType>, include_flag: bool) -> proc_macro2::TokenStream {
|
|
let from_bytes = generate_from_bytes(&attrs);
|
|
let as_bytes = generate_as_bytes(&attrs);
|
|
|
|
quote! {
|
|
impl PSOPacket for #name {
|
|
fn from_bytes(data: &[u8]) -> Result<#name, PacketParseError> {
|
|
let mut cur = std::io::Cursor::new(data);
|
|
let mut b: [u8; 2] = [0; 2];
|
|
cur.read_exact(&mut b).unwrap();
|
|
let len = u16::from_le_bytes(b);
|
|
cur.read_exact(&mut b).unwrap();
|
|
let cmd = u16::from_le_bytes(b);
|
|
let mut f: [u8; 4] = [0; 4];
|
|
|
|
let flag = if #include_flag {
|
|
cur.read_exact(&mut f).unwrap();
|
|
u32::from_le_bytes(f)
|
|
}
|
|
else { 0 };
|
|
|
|
if cmd != #pkt_cmd {
|
|
return Err(PacketParseError::WrongPacketCommand {expected: #pkt_cmd, got: cmd});
|
|
}
|
|
|
|
if len as usize != data.len() {
|
|
return Err(PacketParseError::WrongPacketSize(len, data.len()));
|
|
}
|
|
|
|
let result = Ok(#name {
|
|
#(#from_bytes)*
|
|
});
|
|
|
|
if cur.position() as usize != data.len() {
|
|
return Err(PacketParseError::DataStructNotLargeEnough(cur.position(), data.len()));
|
|
}
|
|
|
|
result
|
|
}
|
|
|
|
fn as_bytes(&self) -> Vec<u8> {
|
|
let mut buf = Vec::new();
|
|
let mut flag = 0;
|
|
#(#as_bytes)*
|
|
|
|
while buf.len() % 4 != 0 {
|
|
buf.push(0);
|
|
}
|
|
|
|
let pkt_len = (buf.len() + if #include_flag { 8 } else { 4 }) as u16;
|
|
let mut prebuf: Vec<u8> = Vec::new();
|
|
|
|
prebuf.extend_from_slice(&u16::to_le_bytes(pkt_len));
|
|
prebuf.extend_from_slice(&u16::to_le_bytes(#pkt_cmd));
|
|
if #include_flag {
|
|
prebuf.extend_from_slice(&u32::to_le_bytes(flag));
|
|
}
|
|
prebuf.append(&mut buf);
|
|
prebuf
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn generate_debug_impl(name: syn::Ident, attrs: &Vec<AttrType>) -> proc_macro2::TokenStream {
|
|
let dbg_write = attrs
|
|
.iter()
|
|
.map(|attr| {
|
|
match attr {
|
|
AttrType::Value(ty, name, meta) => {
|
|
let ident_str = name.to_string();
|
|
let type_str = ty.path.segments[0].ident.to_string();
|
|
match meta {
|
|
AttrMeta::NoDebug => quote! {
|
|
.field(&format!("{} [{}]", #ident_str, #type_str), &format_args!("[...]"))
|
|
},
|
|
_ => quote! {
|
|
.field(&format!("{} [{}]", #ident_str, #type_str), &self.#name)
|
|
}
|
|
}
|
|
},
|
|
AttrType::Array(ty, name, len, meta) => {
|
|
let ident_str = name.to_string();
|
|
let type_str = ty.path.segments[0].ident.to_string();
|
|
match meta {
|
|
AttrMeta::Utf8 => quote! {
|
|
.field(&format!("{} [utf8; {}]", #ident_str, #len),
|
|
match std::str::from_utf8(&self.#name) {
|
|
Ok(ref s) => s,
|
|
Err(_) => &self.#name
|
|
})
|
|
},
|
|
AttrMeta::Utf16 => quote! {
|
|
.field(&format!("{} [utf16; {}]", #ident_str, #len),
|
|
match std::str::from_utf16(&self.#name) {
|
|
Ok(ref s) => s,
|
|
Err(_) => &self.#name
|
|
})
|
|
},
|
|
AttrMeta::NoDebug => quote! {
|
|
.field(&format!("{} [{}; {}]", #ident_str, #type_str, #len), &format_args!("[...]"))
|
|
},
|
|
_ => quote! {
|
|
.field(&format!("{} [{}; {}]", #ident_str, #type_str, #len), &format_args!("{:?}", &self.#name))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
let name_str = name.to_string();
|
|
quote! {
|
|
impl std::fmt::Debug for #name {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
f.debug_struct(#name_str)
|
|
#(#dbg_write)*
|
|
.finish()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
fn generate_partialeq_impl(name: syn::Ident, attrs: &Vec<AttrType>) -> proc_macro2::TokenStream {
|
|
let mut partialeq = Vec::new();
|
|
for attr in attrs {
|
|
let element = match attr {
|
|
AttrType::Value(_, name, _) => {
|
|
quote! {
|
|
if self.#name!= other.#name {
|
|
return false;
|
|
}
|
|
}
|
|
},
|
|
AttrType::Array(_, name, _, _) => {
|
|
quote! {
|
|
if self.#name[..] != other.#name[..] {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
}
|
|
};
|
|
partialeq.push(element);
|
|
}
|
|
quote! {
|
|
impl std::cmp::PartialEq for #name {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
#(#partialeq)*
|
|
true
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
fn get_struct_fields(fields: Iter<Field>) -> Result<Vec<AttrType>, TokenStream> {
|
|
let mut attrs = Vec::new();
|
|
|
|
let mut must_be_last = false;
|
|
for field in fields {
|
|
if must_be_last {
|
|
return Err(syn::Error::new(field.ident.as_ref().unwrap().span(), "variables can not follow Vec or String").to_compile_error().into());
|
|
}
|
|
let mut attr_meta = AttrMeta::None;
|
|
for attr in &field.attrs {
|
|
attr_meta = match attr.path().segments[0].ident.to_string().as_str() {
|
|
"utf8" => AttrMeta::Utf8,
|
|
"utf16" => AttrMeta::Utf16,
|
|
"nodebug" => AttrMeta::NoDebug,
|
|
"length_is" => AttrMeta::LengthIs(attr.parse_args::<syn::Ident>().unwrap()),
|
|
"length_of" => AttrMeta::LengthOf(attr.parse_args::<syn::Ident>().unwrap()),
|
|
_ => AttrMeta::None
|
|
}
|
|
}
|
|
match &field.ty {
|
|
syn::Type::Array(ty) => {
|
|
if let (syn::Type::Path(ref ty), syn::Expr::Lit(ref lit)) = (&*ty.elem, &ty.len) {
|
|
if let syn::Lit::Int(ref int) = lit.lit {
|
|
attrs.push(AttrType::Array(ty.clone(),
|
|
field.ident.as_ref().unwrap().clone(),
|
|
int.base10_parse().unwrap(),
|
|
attr_meta
|
|
))
|
|
}
|
|
}
|
|
},
|
|
syn::Type::Path(ty) => {
|
|
let type_str = ty.path.segments[0].ident.to_string();
|
|
if type_str == "String" || type_str == "Vec"{
|
|
must_be_last = true;
|
|
}
|
|
attrs.push(AttrType::Value(ty.clone(),
|
|
field.ident.as_ref().unwrap().clone(),
|
|
attr_meta))
|
|
},
|
|
_ => {}
|
|
}
|
|
}
|
|
Ok(attrs)
|
|
}
|
|
|
|
|
|
#[derive(Debug)]
|
|
struct PSOPacketAttrs {
|
|
cmd: u16,
|
|
flag: bool,
|
|
manual_flag: bool,
|
|
}
|
|
|
|
impl Parse for PSOPacketAttrs {
|
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
|
let vars = Punctuated::<syn::Expr, syn::Token![,]>::parse_terminated(input)?;
|
|
|
|
let (cmd, flag, manual_flag) = vars
|
|
.into_iter()
|
|
.fold((None, true, false), |mut acc, attr| {
|
|
match attr {
|
|
syn::Expr::Lit(lit) => {
|
|
if let syn::Lit::Int(int) = lit.lit {
|
|
acc.0 = int.base10_parse().ok();
|
|
}
|
|
},
|
|
syn::Expr::Path(path) => {
|
|
if path.path.is_ident("no_flag") {
|
|
acc.1 = false;
|
|
}
|
|
if path.path.is_ident("manual_flag") {
|
|
acc.1 = false;
|
|
acc.2 = true;
|
|
}
|
|
}
|
|
_ => {},
|
|
}
|
|
acc
|
|
});
|
|
Ok(PSOPacketAttrs {
|
|
cmd: cmd.unwrap(),
|
|
flag,
|
|
manual_flag
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[proc_macro_attribute]
|
|
pub fn pso_packet(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|
let args = parse_macro_input!(attr as PSOPacketAttrs);
|
|
|
|
let pkt_struct = parse_macro_input!(item as ItemStruct);
|
|
let attrs = match get_struct_fields(pkt_struct.fields.iter()) {
|
|
Ok(a) => a,
|
|
Err(err) => return err
|
|
};
|
|
|
|
if args.manual_flag {
|
|
match &attrs[0] {
|
|
AttrType::Array(_, ident, _, _) => {
|
|
if ident.to_string() != "flag" {
|
|
return syn::Error::new(pkt_struct.ident.span(), "struct must have flag as the first field if manual_flag is set").to_compile_error().into();
|
|
}
|
|
},
|
|
AttrType::Value(_, ident, _) => {
|
|
if ident.to_string() != "flag" {
|
|
return syn::Error::new(pkt_struct.ident.span(), "struct must have flag as the first field if manual_flag is set").to_compile_error().into();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let struct_def = generate_struct_def(pkt_struct.ident.clone(), &attrs);
|
|
let psopacket_impl = generate_psopacket_impl(args.cmd, pkt_struct.ident.clone(), &attrs, args.flag);
|
|
let debug_impl = generate_debug_impl(pkt_struct.ident.clone(), &attrs);
|
|
let partialeq_impl = generate_partialeq_impl(pkt_struct.ident.clone(), &attrs);
|
|
|
|
let q = quote!{
|
|
#[derive(Clone)]
|
|
#struct_def
|
|
#psopacket_impl
|
|
#debug_impl
|
|
#partialeq_impl
|
|
};
|
|
|
|
q.into()
|
|
}
|
|
|
|
|
|
fn generate_psomessage_from_bytes(attrs: &Vec<AttrType>) -> (Vec<proc_macro2::TokenStream>, Vec<proc_macro2::TokenStream>) {
|
|
//let mut from_bytes = Vec::new();
|
|
let mut assignment = Vec::new();
|
|
let mut structure_creation = Vec::new();
|
|
for attr in attrs {
|
|
let (assignment_element, creation_element) = match attr {
|
|
AttrType::Value(ty, name, meta) => {
|
|
let temp_name = syn::Ident::new(&("_".to_string() + &name.to_string()), name.span());
|
|
let type_str = ty.path.segments[0].ident.to_string();
|
|
if type_str == "Vec" {
|
|
let vec_type = match &ty.path.segments[0].arguments {
|
|
syn::PathArguments::AngleBracketed(arg) => {
|
|
match &arg.args[0] {
|
|
syn::GenericArgument::Type(typ) => {
|
|
match &typ {
|
|
syn::Type::Path(path) => {
|
|
Some(path.path.segments[0].ident.clone())
|
|
}
|
|
_ => None
|
|
}
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
_ => None
|
|
}.unwrap();
|
|
|
|
let length_variable = match meta {
|
|
AttrMeta::LengthIs(ident) => syn::Ident::new(&("_".to_string() + &ident.to_string()), ident.span()),
|
|
_ => panic!("{} does not have a length specified", name),
|
|
};
|
|
let assignment = quote! {
|
|
let mut #temp_name = Vec::new();
|
|
|
|
for _ in 0..#length_variable {
|
|
#temp_name.push(<#vec_type as PSOPacketData>::from_bytes(&mut cur)?);
|
|
}
|
|
};
|
|
let creation = quote! {
|
|
#name: #temp_name,
|
|
};
|
|
|
|
(assignment, creation)
|
|
}
|
|
else {
|
|
let assignment = quote! {
|
|
let #temp_name = <#ty as PSOPacketData>::from_bytes(&mut cur)?;
|
|
};
|
|
let creation = quote! {
|
|
#name: #temp_name,
|
|
};
|
|
|
|
(assignment, creation)
|
|
}
|
|
},
|
|
AttrType::Array(ty, name, len, _) => {
|
|
let temp_name = syn::Ident::new(&("_".to_string() + &name.to_string()), name.span());
|
|
let assignment = quote! {
|
|
let #temp_name = {
|
|
let mut arr = [#ty::default(); #len];
|
|
for e in arr.iter_mut() {
|
|
*e = #ty::from_bytes(&mut cur)?
|
|
}
|
|
arr
|
|
};
|
|
};
|
|
let creation = quote! {
|
|
#name: #temp_name,
|
|
};
|
|
(assignment, creation)
|
|
}
|
|
};
|
|
assignment.push(assignment_element);
|
|
structure_creation.push(creation_element);
|
|
}
|
|
(assignment, structure_creation)
|
|
}
|
|
|
|
fn generate_psomessage_as_bytes(attrs: &Vec<AttrType>) -> Vec<proc_macro2::TokenStream> {
|
|
let mut as_bytes = Vec::new();
|
|
for attr in attrs {
|
|
let element = match attr {
|
|
AttrType::Value(ty, name, meta) => {
|
|
let type_str = ty.path.segments[0].ident.to_string();
|
|
if type_str == "Vec" {
|
|
quote! {
|
|
for i in self.#name.iter() {
|
|
buf.extend_from_slice(&PSOPacketData::as_bytes(i));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if let AttrMeta::LengthOf(ident) = meta {
|
|
quote! {
|
|
buf.extend_from_slice(&PSOPacketData::as_bytes(&(self.#ident.len() as #ty)));
|
|
}
|
|
}
|
|
else {
|
|
quote! {
|
|
buf.extend_from_slice(&PSOPacketData::as_bytes(&self.#name));
|
|
}
|
|
}
|
|
}
|
|
},
|
|
AttrType::Array(_ty, name, len, _) => {
|
|
quote! {
|
|
for i in 0..#len {
|
|
buf.extend_from_slice(&self.#name[i].as_bytes());
|
|
}
|
|
}
|
|
}
|
|
};
|
|
as_bytes.push(element);
|
|
}
|
|
as_bytes
|
|
}
|
|
|
|
|
|
|
|
|
|
fn generate_psomessage_impl(msg_cmd: u8, name: syn::Ident, attrs: &Vec<AttrType>) -> proc_macro2::TokenStream {
|
|
let (assignment, struct_creation)= generate_psomessage_from_bytes(&attrs);
|
|
let as_bytes = generate_psomessage_as_bytes(&attrs);
|
|
|
|
quote! {
|
|
impl PSOMessage for #name {
|
|
const CMD: u8 = #msg_cmd;
|
|
fn from_bytes<R: std::io::Read + std::io::Seek >(mut cur: &mut R) -> Result<#name, PacketParseError> {
|
|
let mut buf1 = [0u8; 1];
|
|
cur.read_exact(&mut buf1).unwrap();
|
|
let cmd = buf1[0];
|
|
cur.read_exact(&mut buf1).unwrap();
|
|
let size = buf1[0];
|
|
|
|
let mut subbuf = vec![0u8; size as usize * 4 - 2];
|
|
let len = cur.read(&mut subbuf).unwrap();
|
|
|
|
if cmd != #msg_cmd {
|
|
return Err(PacketParseError::WrongMessageCommand {expected: #msg_cmd, got: cmd});
|
|
}
|
|
|
|
if len != size as usize * 4 - 2 {
|
|
return Err(PacketParseError::WrongPacketSize(size as u16 * 4, len));
|
|
}
|
|
|
|
let mut cur = std::io::Cursor::new(subbuf);
|
|
#(#assignment)*
|
|
let result = Ok(#name {
|
|
#(#struct_creation)*
|
|
});
|
|
|
|
result
|
|
}
|
|
|
|
fn as_bytes(&self) -> Vec<u8> {
|
|
let mut buf = Vec::new();
|
|
#(#as_bytes)*
|
|
|
|
while buf.len() % 4 != 2 {
|
|
buf.push(0);
|
|
}
|
|
|
|
let mut fullbuf = Vec::new();
|
|
fullbuf.push(#msg_cmd);
|
|
fullbuf.push(((buf.len() + 2) / 4) as u8);
|
|
fullbuf.extend_from_slice(&mut buf);
|
|
|
|
fullbuf
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[proc_macro_attribute]
|
|
pub fn pso_message(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|
let cmd = parse_macro_input!(attr as syn::LitInt).base10_parse().unwrap();
|
|
let pkt_struct = parse_macro_input!(item as ItemStruct);
|
|
|
|
let mut fields = match get_struct_fields(pkt_struct.fields.iter()) {
|
|
Ok(a) => a,
|
|
Err(err) => return err
|
|
};
|
|
|
|
// this is a lot of work to make a `u8` token, surely this can be easier?
|
|
let mut punctuated: syn::punctuated::Punctuated<syn::PathSegment, syn::Token![::]> = syn::punctuated::Punctuated::new();
|
|
punctuated.push_value(syn::PathSegment {
|
|
ident: syn::Ident::new("u8", proc_macro2::Span::call_site()),
|
|
arguments: syn::PathArguments::None,
|
|
});
|
|
let u8tpath = syn::TypePath {
|
|
qself: None,
|
|
path: syn::Path {
|
|
leading_colon: None,
|
|
segments: punctuated
|
|
}
|
|
};
|
|
fields.insert(0, AttrType::Value(u8tpath.clone(), syn::Ident::new("target", proc_macro2::Span::call_site()), AttrMeta::None));
|
|
fields.insert(0, AttrType::Value(u8tpath, syn::Ident::new("client", proc_macro2::Span::call_site()), AttrMeta::None));
|
|
|
|
let struct_def = generate_struct_def(pkt_struct.ident.clone(), &fields);
|
|
let psopacket_impl = generate_psomessage_impl(cmd, pkt_struct.ident.clone(), &fields);
|
|
let debug_impl = generate_debug_impl(pkt_struct.ident.clone(), &fields);
|
|
let partialeq_impl = generate_partialeq_impl(pkt_struct.ident.clone(), &fields);
|
|
|
|
let q = quote!{
|
|
#[derive(Clone)]
|
|
#struct_def
|
|
#psopacket_impl
|
|
#debug_impl
|
|
#partialeq_impl
|
|
};
|
|
|
|
q.into()
|
|
}
|
|
|
|
fn pso_packet_data_struct(name: syn::Ident, fields: syn::Fields) -> TokenStream {
|
|
let attrs = match get_struct_fields(fields.iter()) {
|
|
Ok(a) => a,
|
|
Err(err) => return err
|
|
};
|
|
|
|
let from_bytes = generate_from_bytes(&attrs);
|
|
let as_bytes = generate_as_bytes(&attrs);
|
|
|
|
let impl_pso_data_packet = quote! {
|
|
impl PSOPacketData for #name {
|
|
fn from_bytes<R: std::io::Read + std::io::Seek>(mut cur: &mut R) -> Result<Self, PacketParseError> {
|
|
Ok(#name {
|
|
#(#from_bytes)*
|
|
})
|
|
}
|
|
|
|
fn as_bytes(&self) -> Vec<u8> {
|
|
let mut buf = Vec::new();
|
|
#(#as_bytes)*
|
|
buf
|
|
}
|
|
}
|
|
};
|
|
|
|
let partialeq = generate_partialeq_impl(name.clone(), &attrs);
|
|
let debug = generate_debug_impl(name, &attrs);
|
|
|
|
let q = quote! {
|
|
#impl_pso_data_packet
|
|
#partialeq
|
|
#debug
|
|
};
|
|
|
|
q.into()
|
|
}
|
|
|
|
fn pso_packet_data_enum<'a>(name: syn::Ident, repr_type: syn::Ident, variants: impl Iterator<Item = &'a syn::Variant> + Clone) -> TokenStream {
|
|
let value_to_variant = variants
|
|
.clone()
|
|
.enumerate()
|
|
.map(|(i, variant)| {
|
|
quote! {
|
|
#i => #name::#variant,
|
|
}
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
let variant_to_value = variants
|
|
.enumerate()
|
|
.map(|(i, variant)| {
|
|
quote! {
|
|
#name::#variant => #repr_type::to_le_bytes(#i as #repr_type).to_vec(),
|
|
}
|
|
})
|
|
.collect::<Vec<_>>();
|
|
let impl_pso_data_packet = quote! {
|
|
impl PSOPacketData for #name {
|
|
fn from_bytes<R: std::io::Read + std::io::Seek>(mut cur: &mut R) -> Result<Self, PacketParseError> {
|
|
let mut buf = #repr_type::default().to_le_bytes();
|
|
cur.read_exact(&mut buf).unwrap();
|
|
let value = #repr_type::from_le_bytes(buf);
|
|
|
|
Ok(match value as usize {
|
|
#(#value_to_variant)*
|
|
_ => return Err(PacketParseError::InvalidValue)
|
|
})
|
|
}
|
|
|
|
fn as_bytes(&self) -> Vec<#repr_type> {
|
|
match self {
|
|
#(#variant_to_value)*
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
impl_pso_data_packet.into()
|
|
}
|
|
|
|
#[proc_macro_derive(PSOPacketData)]
|
|
pub fn pso_packet_data(input: TokenStream) -> TokenStream {
|
|
let derive = parse_macro_input!(input as DeriveInput);
|
|
|
|
let name = derive.ident;
|
|
|
|
if let syn::Data::Struct(strct) = derive.data {
|
|
pso_packet_data_struct(name, strct.fields)
|
|
}
|
|
else if let syn::Data::Enum(enm) = derive.data {
|
|
let repr_type = derive.attrs.iter().fold(None, |mut repr_type, attr| {
|
|
if attr.path().is_ident("repr") {
|
|
attr.parse_nested_meta(|meta| {
|
|
repr_type = Some(meta.path.get_ident().cloned().unwrap());
|
|
Ok(())
|
|
}).unwrap();
|
|
}
|
|
repr_type
|
|
});
|
|
|
|
pso_packet_data_enum(name, repr_type.unwrap(), enm.variants.iter())
|
|
}
|
|
else {
|
|
syn::Error::new(name.span(), "PSOPacketData only works on structs and enums").to_compile_error().into()
|
|
}
|
|
}
|