diff --git a/crates/bevy_interleave_macros/src/lib.rs b/crates/bevy_interleave_macros/src/lib.rs index 89307cc..37a33ae 100644 --- a/crates/bevy_interleave_macros/src/lib.rs +++ b/crates/bevy_interleave_macros/src/lib.rs @@ -1,18 +1,14 @@ extern crate proc_macro; use proc_macro::TokenStream; -use quote::quote; use syn::{ - Data, DeriveInput, - Error, - Fields, - FieldsNamed, - Ident, - Result, parse_macro_input, }; +mod planar; +use planar::generate_planar_struct; + #[proc_macro_derive(Planar)] pub fn planar_macro_derive(input: TokenStream) -> TokenStream { @@ -25,129 +21,3 @@ pub fn planar_macro_derive(input: TokenStream) -> TokenStream { TokenStream::from(output) } - -fn generate_planar_struct(input: &DeriveInput) -> Result { - let name = &input.ident; - let planar_name = Ident::new(&format!("Planar{}", name), name.span()); - - let fields_struct = if let Data::Struct(ref data_struct) = input.data { - match data_struct.fields { - Fields::Named(ref fields) => fields, - _ => return Err(Error::new_spanned(input, "Unsupported struct type")), - } - } else { - return Err(Error::new_spanned(input, "Planar macro only supports structs")); - }; - - let field_names = fields_struct.named.iter().map(|f| f.ident.as_ref().unwrap()); - let field_types = fields_struct.named.iter().map(|_| quote! { Vec }); - - let conversion_methods = generate_conversion_methods(name, fields_struct); - let get_set_methods = generate_accessor_setter_methods(name, fields_struct); - let len_method = generate_len_method(fields_struct); - - let expanded = quote! { - pub struct #planar_name { - #(pub #field_names: #field_types,)* - } - - impl Planar<#name> for #planar_name { - #conversion_methods - #get_set_methods - #len_method - } - }; - - Ok(expanded) -} - - -fn generate_len_method(fields_named: &FieldsNamed) -> quote::__private::TokenStream { - if let Some(first_field) = fields_named.named.first() { - let first_field_name = first_field.ident.as_ref().unwrap(); - quote! { - fn is_empty(&self) -> bool { - self.#first_field_name.is_empty() - } - - fn len(&self) -> usize { - self.#first_field_name.len() - } - } - } else { - quote! { - fn is_empty(&self) -> bool { - true - } - - fn len(&self) -> usize { - 0 - } - } - } -} - - -fn generate_accessor_setter_methods(struct_name: &Ident, fields_named: &FieldsNamed) -> quote::__private::TokenStream { - let packed_assignments = fields_named.named.iter().map(|field| { - let name = field.ident.as_ref().unwrap(); - quote! { #name: self.#name[index].clone() } - }); - - let set_assignments = fields_named.named.iter().map(|field| { - let name = field.ident.as_ref().unwrap(); - quote! { self.#name[index] = value.#name.clone(); } - }); - - quote! { - fn get(&self, index: usize) -> #struct_name { - #struct_name { - #(#packed_assignments),* - } - } - - fn set(&mut self, index: usize, value: #struct_name) { - #(#set_assignments);* - } - } -} - - -fn generate_conversion_methods(struct_name: &Ident, fields_named: &FieldsNamed) -> quote::__private::TokenStream { - let ( - from_interleaved_fields, - to_interleaved_fields_templates - ): (Vec<_>, Vec<_>) = fields_named.named.iter().map(|field| { - let name = field.ident.as_ref().unwrap(); - - let from_interleaved_field = quote! { - #name: packed.iter().map(|x| x.#name.clone()).collect() - }; - let to_interleaved_field_template = quote! { - #name: self.#name[index].clone() - }; - - (from_interleaved_field, to_interleaved_field_template) - }).unzip(); - - let to_interleaved_method = quote! { - fn to_interleaved(&self) -> Vec<#struct_name> { - (0..self.len()) - .map(|index| #struct_name { - #(#to_interleaved_fields_templates),* - }) - .collect() - } - }; - - let conversion_methods = quote! { - fn from_interleaved(packed: Vec<#struct_name>) -> Self { - Self { - #(#from_interleaved_fields),* - } - } - #to_interleaved_method - }; - - conversion_methods -} diff --git a/crates/bevy_interleave_macros/src/planar.rs b/crates/bevy_interleave_macros/src/planar.rs new file mode 100644 index 0000000..b6f01a2 --- /dev/null +++ b/crates/bevy_interleave_macros/src/planar.rs @@ -0,0 +1,140 @@ +extern crate proc_macro; + +use quote::quote; +use syn::{ + Data, + DeriveInput, + Error, + Fields, + FieldsNamed, + Ident, + Result, +}; + + +pub fn generate_planar_struct(input: &DeriveInput) -> Result { + let name = &input.ident; + let planar_name = Ident::new(&format!("Planar{}", name), name.span()); + + let fields_struct = if let Data::Struct(ref data_struct) = input.data { + match data_struct.fields { + Fields::Named(ref fields) => fields, + _ => return Err(Error::new_spanned(input, "Unsupported struct type")), + } + } else { + return Err(Error::new_spanned(input, "Planar macro only supports structs")); + }; + + let field_names = fields_struct.named.iter().map(|f| f.ident.as_ref().unwrap()); + let field_types = fields_struct.named.iter().map(|_| quote! { Vec }); + + let conversion_methods = generate_conversion_methods(name, fields_struct); + let get_set_methods = generate_accessor_setter_methods(name, fields_struct); + let len_method = generate_len_method(fields_struct); + + let expanded = quote! { + pub struct #planar_name { + #(pub #field_names: #field_types,)* + } + + impl Planar<#name> for #planar_name { + #conversion_methods + #get_set_methods + #len_method + } + }; + + Ok(expanded) +} + + +pub fn generate_len_method(fields_named: &FieldsNamed) -> quote::__private::TokenStream { + if let Some(first_field) = fields_named.named.first() { + let first_field_name = first_field.ident.as_ref().unwrap(); + quote! { + fn is_empty(&self) -> bool { + self.#first_field_name.is_empty() + } + + fn len(&self) -> usize { + self.#first_field_name.len() + } + } + } else { + quote! { + fn is_empty(&self) -> bool { + true + } + + fn len(&self) -> usize { + 0 + } + } + } +} + + +pub fn generate_accessor_setter_methods(struct_name: &Ident, fields_named: &FieldsNamed) -> quote::__private::TokenStream { + let packed_assignments = fields_named.named.iter().map(|field| { + let name = field.ident.as_ref().unwrap(); + quote! { #name: self.#name[index].clone() } + }); + + let set_assignments = fields_named.named.iter().map(|field| { + let name = field.ident.as_ref().unwrap(); + quote! { self.#name[index] = value.#name.clone(); } + }); + + quote! { + fn get(&self, index: usize) -> #struct_name { + #struct_name { + #(#packed_assignments),* + } + } + + fn set(&mut self, index: usize, value: #struct_name) { + #(#set_assignments);* + } + } +} + + +pub fn generate_conversion_methods(struct_name: &Ident, fields_named: &FieldsNamed) -> quote::__private::TokenStream { + let ( + from_interleaved_fields, + to_interleaved_fields_templates + ): (Vec<_>, Vec<_>) = fields_named.named.iter().map(|field| { + let name = field.ident.as_ref().unwrap(); + + let from_interleaved_field = quote! { + #name: packed.iter().map(|x| x.#name.clone()).collect() + }; + let to_interleaved_field_template = quote! { + #name: self.#name[index].clone() + }; + + (from_interleaved_field, to_interleaved_field_template) + }).unzip(); + + let to_interleaved_method = quote! { + fn to_interleaved(&self) -> Vec<#struct_name> { + (0..self.len()) + .map(|index| #struct_name { + #(#to_interleaved_fields_templates),* + }) + .collect() + } + }; + + let conversion_methods = quote! { + fn from_interleaved(packed: Vec<#struct_name>) -> Self { + Self { + #(#from_interleaved_fields),* + } + } + #to_interleaved_method + }; + + conversion_methods +} +