use quote::quote;
use syn::{Data, DeriveInput, Fields, Type};
use crate::{extract_option_inner_type, extract_packet_attr};
fn generate_read_value(ty: &Type, attr: Option<&str>) -> proc_macro2::TokenStream {
match attr {
Some("varint") => quote! { <i32 as nurtex_codec::VarInt>::read_varint(buffer)? },
Some("varlong") => quote! { <i64 as nurtex_codec::VarLong>::read_varlong(buffer)? },
Some("vec_end") => quote! {
{
let remaining = buffer.get_ref().len() - buffer.position() as usize;
let mut vec = vec![0u8; remaining];
for byte in &mut vec {
*byte = u8::read_buf(buffer)?;
}
vec
}
},
Some("vec_varint") => quote! {
{
let count = <i32 as nurtex_codec::VarInt>::read_varint(buffer)? as usize;
let mut vec = Vec::with_capacity(count);
for _ in 0..count {
vec.push(<i32 as nurtex_codec::VarInt>::read_varint(buffer)?);
}
vec
}
},
Some("vec_varlong") => quote! {
{
let count = <i32 as nurtex_codec::VarInt>::read_varint(buffer)? as usize;
let mut vec = Vec::with_capacity(count);
for _ in 0..count {
vec.push(<i64 as nurtex_codec::VarLong>::read_varlong(buffer)?);
}
vec
}
},
_ => quote! { <#ty as nurtex_codec::Buffer>::read_buf(buffer)? },
}
}
fn generate_write_value(value: proc_macro2::TokenStream, _ty: &Type, attr: Option<&str>) -> proc_macro2::TokenStream {
match attr {
Some("varint") => quote! { <i32 as nurtex_codec::VarInt>::write_varint(&#value, buffer)?; },
Some("varlong") => quote! { <i64 as nurtex_codec::VarLong>::write_varlong(&#value, buffer)?; },
Some("vec_end") => quote! {
<i32 as nurtex_codec::VarInt>::write_varint(&(#value.len() as i32), buffer)?;
for byte in &#value {
byte.write_buf(buffer)?;
}
},
Some("vec_varint") => quote! {
<i32 as nurtex_codec::VarInt>::write_varint(&(#value.len() as i32), buffer)?;
for item in &#value {
<i32 as nurtex_codec::VarInt>::write_varint(item, buffer)?;
}
},
Some("vec_varlong") => quote! {
<i32 as nurtex_codec::VarInt>::write_varint(&(#value.len() as i32), buffer)?;
for item in &#value {
<i64 as nurtex_codec::VarLong>::write_varlong(item, buffer)?;
}
},
_ => quote! { #value.write_buf(buffer)?; },
}
}
pub fn generate_read(input: &DeriveInput) -> proc_macro2::TokenStream {
match &input.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => {
let field_reads = fields.named.iter().map(|f| {
let name = &f.ident;
let ty = &f.ty;
let attr = extract_packet_attr(f);
if let Some(inner_ty) = extract_option_inner_type(ty) {
let read_value = generate_read_value(&inner_ty, attr.as_deref());
quote! {
#name: if <bool as nurtex_codec::Buffer>::read_buf(buffer)? {
Some(#read_value)
} else {
None
}
}
} else {
let read_value = generate_read_value(ty, attr.as_deref());
quote! { #name: #read_value }
}
});
quote! {
Some(Self {
#(#field_reads),*
})
}
}
Fields::Unit => quote! { Some(Self) },
_ => quote! { compile_error!("Packet derive only supports named fields") },
},
_ => quote! { compile_error!("Packet derive only supports structs") },
}
}
pub fn generate_write(input: &DeriveInput) -> proc_macro2::TokenStream {
match &input.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => {
let field_writes = fields.named.iter().map(|f| {
let name = &f.ident;
let ty = &f.ty;
let attr = extract_packet_attr(f);
if let Some(inner_ty) = extract_option_inner_type(ty) {
let write_value = generate_write_value(quote! { val }, &inner_ty, attr.as_deref());
quote! {
<bool as nurtex_codec::Buffer>::write_buf(&self.#name.is_some(), buffer)?;
if let Some(val) = &self.#name {
#write_value
}
}
} else {
let write_value = generate_write_value(quote! { self.#name }, ty, attr.as_deref());
quote! { #write_value }
}
});
quote! {
#(#field_writes)*
}
}
Fields::Unit => quote! {},
_ => quote! { compile_error!("Packet derive only supports named fields") },
},
_ => quote! { compile_error!("Packet derive only supports structs") },
}
}