use proc_macro::TokenStream;
#[proc_macro_derive(ServerboundPacket, attributes(packet))]
pub fn serverbound_packet_derive(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let id = input.attrs
.iter()
.find(|attr| attr.path().is_ident("packet"))
.and_then(|attr| {
let mut id = None;
let _ = attr.parse_nested_meta(|meta| {
if meta.path.is_ident("id") {
if let Ok(value) = meta.value() {
if let Ok(lit) = value.parse::<syn::LitInt>() {
if let Ok(num) = lit.base10_parse::<i32>() {
id = Some(num);
}
}
}
}
Ok(())
});
id
});
if let syn::Data::Struct(data) = &input.data {
let struct_name = &input.ident;
let names: Vec<_> = data.fields.iter().map(|f| &f.ident).collect();
let types: Vec<_> = data.fields.iter().map(|f| &f.ty).collect();
let expanded = quote::quote! {
impl ::mcproto::packet::ServerboundPacket for #struct_name {
fn packet_id(&self) -> i32 {
#id
}
fn encode(&self, buf: &mut impl std::io::Write) -> Result<(), ::mcproto::CodecError> {
#(
<#types as ::mcproto::PacketCodec>::encode(&self.#names, buf)?;
)*
Ok(())
}
fn decode(buf: &mut impl std::io::Read) -> Result<Self, ::mcproto::CodecError> {
Ok(Self {
#(
#names: <#types as ::mcproto::PacketCodec>::decode(buf)?,
)*
})
}
}
};
TokenStream::from(expanded)
} else {
syn::Error::new_spanned(&input.ident, "ServerboundPacket can only be derived for structs").to_compile_error().into()
}
}