mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
add support for unnamed structs to azalea-buf-macros
This commit is contained in:
parent
4e5c551d65
commit
d0b459e827
3 changed files with 156 additions and 96 deletions
|
@ -1,62 +1,6 @@
|
||||||
use quote::{ToTokens, quote};
|
use quote::{ToTokens, quote};
|
||||||
use syn::{Data, Field, FieldsNamed, Ident, punctuated::Punctuated, token::Comma};
|
use syn::{Data, Field, FieldsNamed, Ident, punctuated::Punctuated, token::Comma};
|
||||||
|
|
||||||
fn read_named_fields(
|
|
||||||
named: &Punctuated<Field, Comma>,
|
|
||||||
) -> (Vec<proc_macro2::TokenStream>, Vec<&Option<Ident>>) {
|
|
||||||
let read_fields = named
|
|
||||||
.iter()
|
|
||||||
.map(|f| {
|
|
||||||
let field_name = &f.ident;
|
|
||||||
let field_type = &f.ty;
|
|
||||||
|
|
||||||
let is_variable_length = f.attrs.iter().any(|a| a.path().is_ident("var"));
|
|
||||||
let limit = f
|
|
||||||
.attrs
|
|
||||||
.iter()
|
|
||||||
.find(|a| a.path().is_ident("limit"))
|
|
||||||
.map(|a| {
|
|
||||||
a.parse_args::<syn::LitInt>()
|
|
||||||
.unwrap()
|
|
||||||
.base10_parse::<usize>()
|
|
||||||
.unwrap()
|
|
||||||
});
|
|
||||||
|
|
||||||
if is_variable_length && limit.is_some() {
|
|
||||||
panic!("Fields cannot have both var and limit attributes");
|
|
||||||
}
|
|
||||||
|
|
||||||
// do a different buf.write_* for each field depending on the type
|
|
||||||
// if it's a string, use buf.write_string
|
|
||||||
match field_type {
|
|
||||||
syn::Type::Path(_) | syn::Type::Array(_) => {
|
|
||||||
if is_variable_length {
|
|
||||||
quote! {
|
|
||||||
let #field_name = azalea_buf::AzaleaReadVar::azalea_read_var(buf)?;
|
|
||||||
}
|
|
||||||
} else if let Some(limit) = limit {
|
|
||||||
quote! {
|
|
||||||
let #field_name = azalea_buf::AzaleaReadLimited::azalea_read_limited(buf, #limit)?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote! {
|
|
||||||
let #field_name = azalea_buf::AzaleaRead::azalea_read(buf)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => panic!(
|
|
||||||
"Error reading field {}: {}",
|
|
||||||
field_name.clone().unwrap(),
|
|
||||||
field_type.to_token_stream()
|
|
||||||
),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let read_field_names = named.iter().map(|f| &f.ident).collect::<Vec<_>>();
|
|
||||||
|
|
||||||
(read_fields, read_field_names)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_impl_azalearead(ident: &Ident, data: &Data) -> proc_macro2::TokenStream {
|
pub fn create_impl_azalearead(ident: &Ident, data: &Data) -> proc_macro2::TokenStream {
|
||||||
match data {
|
match data {
|
||||||
syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
|
syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
|
||||||
|
@ -83,8 +27,18 @@ pub fn create_impl_azalearead(ident: &Ident, data: &Data) -> proc_macro2::TokenS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
syn::Fields::Unnamed(fields) => {
|
||||||
panic!("#[derive(AzBuf)] can only be used on structs with named fields")
|
let read_fields = read_unnamed_fields(&fields.unnamed);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl azalea_buf::AzaleaRead for #ident {
|
||||||
|
fn azalea_read(buf: &mut std::io::Cursor<&[u8]>) -> Result<Self, azalea_buf::BufReadError> {
|
||||||
|
Ok(Self(
|
||||||
|
#(#read_fields),*
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
syn::Data::Enum(syn::DataEnum { variants, .. }) => {
|
syn::Data::Enum(syn::DataEnum { variants, .. }) => {
|
||||||
|
@ -202,3 +156,82 @@ pub fn create_impl_azalearead(ident: &Ident, data: &Data) -> proc_macro2::TokenS
|
||||||
_ => panic!("#[derive(AzBuf)] can only be used on structs"),
|
_ => panic!("#[derive(AzBuf)] can only be used on structs"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_named_fields(
|
||||||
|
named: &Punctuated<Field, Comma>,
|
||||||
|
) -> (Vec<proc_macro2::TokenStream>, Vec<&Option<Ident>>) {
|
||||||
|
let read_fields = named
|
||||||
|
.iter()
|
||||||
|
.map(|f| {
|
||||||
|
let field_name = &f.ident;
|
||||||
|
|
||||||
|
let reader_call = get_reader_call(f);
|
||||||
|
quote! { let #field_name = #reader_call; }
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let read_field_names = named.iter().map(|f| &f.ident).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
(read_fields, read_field_names)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_unnamed_fields(unnamed: &Punctuated<Field, Comma>) -> Vec<proc_macro2::TokenStream> {
|
||||||
|
let read_fields = unnamed
|
||||||
|
.iter()
|
||||||
|
.map(|f| {
|
||||||
|
let reader_call = get_reader_call(f);
|
||||||
|
quote! { #reader_call }
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
read_fields
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_reader_call(f: &Field) -> proc_macro2::TokenStream {
|
||||||
|
let is_variable_length = f
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.any(|a: &syn::Attribute| a.path().is_ident("var"));
|
||||||
|
let limit = f
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.find(|a| a.path().is_ident("limit"))
|
||||||
|
.map(|a| {
|
||||||
|
a.parse_args::<syn::LitInt>()
|
||||||
|
.unwrap()
|
||||||
|
.base10_parse::<usize>()
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
if is_variable_length && limit.is_some() {
|
||||||
|
panic!("Fields cannot have both var and limit attributes");
|
||||||
|
}
|
||||||
|
|
||||||
|
let field_type = &f.ty;
|
||||||
|
|
||||||
|
// do a different buf.write_* for each field depending on the type
|
||||||
|
// if it's a string, use buf.write_string
|
||||||
|
let reader_call = match field_type {
|
||||||
|
syn::Type::Path(_) | syn::Type::Array(_) => {
|
||||||
|
if is_variable_length {
|
||||||
|
quote! {
|
||||||
|
azalea_buf::AzaleaReadVar::azalea_read_var(buf)?
|
||||||
|
}
|
||||||
|
} else if let Some(limit) = limit {
|
||||||
|
quote! {
|
||||||
|
azalea_buf::AzaleaReadLimited::azalea_read_limited(buf, #limit)?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
azalea_buf::AzaleaRead::azalea_read(buf)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!(
|
||||||
|
"Error reading field {:?}: {}",
|
||||||
|
f.ident.clone(),
|
||||||
|
field_type.to_token_stream()
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
reader_call
|
||||||
|
}
|
||||||
|
|
|
@ -2,41 +2,6 @@ use proc_macro2::Span;
|
||||||
use quote::{ToTokens, quote};
|
use quote::{ToTokens, quote};
|
||||||
use syn::{Data, Field, FieldsNamed, Ident, punctuated::Punctuated, token::Comma};
|
use syn::{Data, Field, FieldsNamed, Ident, punctuated::Punctuated, token::Comma};
|
||||||
|
|
||||||
fn write_named_fields(
|
|
||||||
named: &Punctuated<Field, Comma>,
|
|
||||||
ident_name: Option<&Ident>,
|
|
||||||
) -> proc_macro2::TokenStream {
|
|
||||||
let write_fields = named.iter().map(|f| {
|
|
||||||
let field_name = &f.ident;
|
|
||||||
let field_type = &f.ty;
|
|
||||||
let ident_dot_field = match ident_name {
|
|
||||||
Some(ident) => quote! { &#ident.#field_name },
|
|
||||||
None => quote! { #field_name },
|
|
||||||
};
|
|
||||||
// do a different buf.write_* for each field depending on the type
|
|
||||||
// if it's a string, use buf.write_string
|
|
||||||
match field_type {
|
|
||||||
syn::Type::Path(_) | syn::Type::Array(_) => {
|
|
||||||
if f.attrs.iter().any(|attr| attr.path().is_ident("var")) {
|
|
||||||
quote! {
|
|
||||||
azalea_buf::AzaleaWriteVar::azalea_write_var(#ident_dot_field, buf)?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote! {
|
|
||||||
azalea_buf::AzaleaWrite::azalea_write(#ident_dot_field, buf)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => panic!(
|
|
||||||
"Error writing field {}: {}",
|
|
||||||
field_name.clone().unwrap(),
|
|
||||||
field_type.to_token_stream()
|
|
||||||
),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
quote! { #(#write_fields)* }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_impl_azaleawrite(ident: &Ident, data: &Data) -> proc_macro2::TokenStream {
|
pub fn create_impl_azaleawrite(ident: &Ident, data: &Data) -> proc_macro2::TokenStream {
|
||||||
match data {
|
match data {
|
||||||
syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
|
syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
|
||||||
|
@ -62,8 +27,17 @@ pub fn create_impl_azaleawrite(ident: &Ident, data: &Data) -> proc_macro2::Token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
syn::Fields::Unnamed(fields) => {
|
||||||
panic!("#[derive(AzBuf)] can only be used on structs with named fields")
|
let write_fields = write_unnamed_fields(&fields.unnamed);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl azalea_buf::AzaleaWrite for #ident {
|
||||||
|
fn azalea_write(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> {
|
||||||
|
#write_fields
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
syn::Data::Enum(syn::DataEnum { variants, .. }) => {
|
syn::Data::Enum(syn::DataEnum { variants, .. }) => {
|
||||||
|
@ -200,3 +174,56 @@ pub fn create_impl_azaleawrite(ident: &Ident, data: &Data) -> proc_macro2::Token
|
||||||
_ => panic!("#[derive(AzBuf)] can only be used on structs"),
|
_ => panic!("#[derive(AzBuf)] can only be used on structs"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_named_fields(
|
||||||
|
named: &Punctuated<Field, Comma>,
|
||||||
|
ident_name: Option<&Ident>,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
let write_fields = named.iter().map(|f| {
|
||||||
|
let field_name = &f.ident;
|
||||||
|
let ident_dot_field = match ident_name {
|
||||||
|
Some(ident) => quote! { &#ident.#field_name },
|
||||||
|
None => quote! { #field_name },
|
||||||
|
};
|
||||||
|
|
||||||
|
make_write_call(f, ident_dot_field)
|
||||||
|
});
|
||||||
|
quote! { #(#write_fields)* }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_unnamed_fields(named: &Punctuated<Field, Comma>) -> proc_macro2::TokenStream {
|
||||||
|
let write_fields = named.iter().enumerate().map(|(i, f)| {
|
||||||
|
let i_literal = syn::Index::from(i);
|
||||||
|
let ident_dot_field = quote! { &self.#i_literal };
|
||||||
|
|
||||||
|
make_write_call(f, ident_dot_field)
|
||||||
|
});
|
||||||
|
quote! { #(#write_fields)* }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_write_call(
|
||||||
|
f: &Field,
|
||||||
|
ident_dot_field: proc_macro2::TokenStream,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
let field_type = &f.ty;
|
||||||
|
// do a different buf.write_* for each field depending on the type
|
||||||
|
// if it's a string, use buf.write_string
|
||||||
|
match field_type {
|
||||||
|
syn::Type::Path(_) | syn::Type::Array(_) => {
|
||||||
|
if f.attrs.iter().any(|attr| attr.path().is_ident("var")) {
|
||||||
|
quote! {
|
||||||
|
azalea_buf::AzaleaWriteVar::azalea_write_var(#ident_dot_field, buf)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
azalea_buf::AzaleaWrite::azalea_write(#ident_dot_field, buf)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!(
|
||||||
|
"Error writing field {:?}: {}",
|
||||||
|
f.ident,
|
||||||
|
field_type.to_token_stream()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use azalea_buf::AzBuf;
|
use azalea_buf::AzBuf;
|
||||||
use azalea_inventory::{operations::ClickType, ItemStack};
|
use azalea_inventory::{ItemStack, operations::ClickType};
|
||||||
use azalea_protocol_macros::ServerboundGamePacket;
|
use azalea_protocol_macros::ServerboundGamePacket;
|
||||||
|
|
||||||
#[derive(Clone, Debug, AzBuf, ServerboundGamePacket)]
|
#[derive(Clone, Debug, AzBuf, ServerboundGamePacket)]
|
||||||
|
|
Loading…
Add table
Reference in a new issue