mod attrs;
mod codegen;
mod decode;
mod encode;
mod packet;
mod size;
use proc_macro::TokenStream;
use syn::{DeriveInput, parse_macro_input};
struct PacketAttrArgs {
id: i32,
}
impl syn::parse::Parse for PacketAttrArgs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let ident: syn::Ident = input.parse()?;
if ident != "id" {
return Err(syn::Error::new(ident.span(), "expected `id`"));
}
let _eq: syn::Token![=] = input.parse()?;
let negative = input.peek(syn::Token![-]);
if negative {
let _neg: syn::Token![-] = input.parse()?;
let lit: syn::LitInt = input.parse()?;
Ok(Self {
id: -(lit.base10_parse::<i32>()?),
})
} else {
let lit: syn::LitInt = input.parse()?;
Ok(Self {
id: lit.base10_parse::<i32>()?,
})
}
}
}
#[proc_macro_attribute]
pub fn packet(attr: TokenStream, item: TokenStream) -> TokenStream {
let packet_id = match syn::parse::<PacketAttrArgs>(attr) {
Ok(args) => args.id,
Err(err) => return err.to_compile_error().into(),
};
let input = parse_macro_input!(item as DeriveInput);
match packet::expand_packet(&input, packet_id) {
Ok(tokens) => {
let name = &input.ident;
let vis = &input.vis;
let attrs: Vec<_> = input
.attrs
.iter()
.filter(|a| !a.path().is_ident("packet"))
.collect();
let generics = &input.generics;
let struct_def = match &input.data {
syn::Data::Struct(data) => match &data.fields {
syn::Fields::Named(fields) => {
let clean_fields: Vec<_> = fields
.named
.iter()
.map(|f| {
let field_attrs: Vec<_> = f
.attrs
.iter()
.filter(|a| !a.path().is_ident("field"))
.collect();
let vis = &f.vis;
let name = &f.ident;
let ty = &f.ty;
quote::quote! {
#(#field_attrs)*
#vis #name: #ty
}
})
.collect();
quote::quote! {
#(#attrs)*
#vis struct #name #generics {
#(#clean_fields),*
}
}
}
syn::Fields::Unit => {
quote::quote! {
#(#attrs)*
#vis struct #name #generics;
}
}
syn::Fields::Unnamed(fields) => {
let fields = &fields.unnamed;
quote::quote! {
#(#attrs)*
#vis struct #name #generics(#fields);
}
}
},
_ => {
return syn::Error::new_spanned(input, "#[packet] can only be used on structs")
.to_compile_error()
.into();
}
};
let combined = quote::quote! {
#struct_def
#tokens
};
combined.into()
}
Err(err) => err.to_compile_error().into(),
}
}
#[proc_macro_derive(Encode, attributes(field, variant))]
pub fn derive_encode(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
match encode::derive_encode(&input) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
}
#[proc_macro_derive(Decode, attributes(field, variant))]
pub fn derive_decode(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
match decode::derive_decode(&input) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
}
#[proc_macro_derive(EncodedSize, attributes(field, variant))]
pub fn derive_encoded_size(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
match size::derive_encoded_size(&input) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
}