use proc_macro2;
use quote::{quote};
use syn::{DeriveInput, Ident};
use syn::parse::{Parse, ParseStream};
pub struct PacketId {
pub value: u32,
}
impl Parse for PacketId {
fn parse(input: ParseStream) -> syn::Result<Self> {
let ident: syn::Ident = input.parse()?;
if ident != "id" {
return Err(syn::Error::new(ident.span(), "expected `id`"));
}
input.parse::<syn::Token![=]>()?;
let lit: syn::LitInt = input.parse()?;
let value = lit.base10_parse::<u32>()?;
Ok(PacketId { value })
}
}
pub struct PacketConfig {
pub packets: Vec<Ident>,
pub null_packet: Ident,
}
impl Parse for PacketConfig {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
syn::bracketed!(content in input);
let packets = content.parse_terminated(Ident::parse, syn::Token![,])?;
input.parse::<syn::Token![,]>()?;
let null_packet = input.parse::<Ident>()?;
Ok(PacketConfig {
packets: packets.into_iter().collect(),
null_packet,
})
}
}
pub fn implement_packet(packet_id: u32, input: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
let struct_name = &input.ident;
let serialize_fields = parse_serialize_fields(input)?;
let packet_impl = generate_packet_impl(struct_name, packet_id, &serialize_fields);
Ok(quote! {
#input
#packet_impl
})
}
fn parse_serialize_fields(input: &DeriveInput) -> syn::Result<Vec<proc_macro2::TokenStream>> {
let mut serialize_fields = Vec::new();
if let syn::Data::Struct(data_struct) = &input.data {
if let syn::Fields::Named(fields_named) = &data_struct.fields {
for field in &fields_named.named {
let field_name = field.ident.as_ref().unwrap();
serialize_fields.push(quote! {
#field_name
});
}
}
}
Ok(serialize_fields)
}
fn generate_packet_impl(struct_name: &Ident, packet_id: u32, serialize_fields: &[proc_macro2::TokenStream]) -> proc_macro2::TokenStream {
quote! {
impl qexed_tcp_connect::net_types::packet::Packet for #struct_name {
fn id(&self) -> u32 {
#packet_id
}
fn serialize(&self, w: &mut qexed_tcp_connect::packet::encode::PacketWriter) {
#(w.serialize(&self.#serialize_fields);)*
}
fn deserialize(&mut self, r: &mut qexed_tcp_connect::packet::decode::PacketReader) {
#(self.#serialize_fields = r.deserialize();)*
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
}
}
pub fn generate_match_arms(packets: &[Ident]) -> Vec<proc_macro2::TokenStream> {
packets.iter().enumerate().map(|(index, packet_name)| {
let packet_id = index as u32;
quote! {
#packet_id => Box::new(#packet_name::new()),
}
}).collect()
}
pub fn implement_subdata(input: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
let struct_name = &input.ident;
let serialize_fields = parse_serialize_fields(input)?;
let subdata_impl = generate_subdata_impl(struct_name, &serialize_fields);
Ok(quote! {
#input
#subdata_impl
})
}
fn generate_subdata_impl(struct_name: &Ident, serialize_fields: &[proc_macro2::TokenStream]) -> proc_macro2::TokenStream {
quote! {
impl qexed_tcp_connect::net_types::subdata::Subdata for #struct_name {
fn new() -> Self {
Self::default()
}
fn serialize(&self, w: &mut qexed_tcp_connect::packet::encode::PacketWriter) {
#(w.serialize(&self.#serialize_fields);)*
}
fn deserialize(&mut self, r: &mut qexed_tcp_connect::packet::decode::PacketReader) {
#(self.#serialize_fields = r.deserialize();)*
}
}
}
}
pub fn generate_function_with_consts(
packets: &[Ident],
null_packet: &Ident
) -> proc_macro2::TokenStream {
let match_arms = generate_match_arms(packets);
let const_defs = generate_const_definitions(packets);
quote! {
#(#const_defs)*
pub fn id_to_packet(id: u32) -> Box<dyn qexed_tcp_connect::net_types::packet::Packet> {
match id {
#(#match_arms)*
_ => {
log::warn!("Unknown packet ID: 0x{:X}, returning {} instance", id, stringify!(#null_packet));
Box::new(#null_packet::new())
},
}
}
}
}
fn generate_const_definitions(packets: &[Ident]) -> Vec<proc_macro2::TokenStream> {
packets.iter().enumerate().map(|(index, packet_name)| {
let packet_id = index as u32;
let const_name = format!("ID_{}", packet_name);
let const_ident = Ident::new(&const_name, packet_name.span());
quote! {
#[allow(dead_code)]
pub const #const_ident: u32 = #packet_id;
}
}).collect()
}