summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engine-macros/src/lib.rs2
-rw-r--r--engine-macros/src/reflection.rs61
-rw-r--r--engine-macros/src/reflection/default_value.rs4
-rw-r--r--engine-macros/src/reflection/enum_impl.rs477
-rw-r--r--engine-macros/src/reflection/field.rs24
-rw-r--r--engine-macros/src/reflection/options_attr.rs37
-rw-r--r--engine-macros/src/reflection/struct_impl.rs105
-rw-r--r--engine-macros/src/util.rs24
8 files changed, 439 insertions, 295 deletions
diff --git a/engine-macros/src/lib.rs b/engine-macros/src/lib.rs
index 5d16ad2..deddd15 100644
--- a/engine-macros/src/lib.rs
+++ b/engine-macros/src/lib.rs
@@ -3,7 +3,7 @@
mod reflection;
mod util;
-#[proc_macro_derive(Reflection)]
+#[proc_macro_derive(Reflection, attributes(reflection))]
pub fn reflection_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream
{
reflection::derive(input.into()).into()
diff --git a/engine-macros/src/reflection.rs b/engine-macros/src/reflection.rs
index 5d40e0c..2180084 100644
--- a/engine-macros/src/reflection.rs
+++ b/engine-macros/src/reflection.rs
@@ -1,6 +1,9 @@
+use crate::reflection::options_attr::OptionsAttr;
+
mod default_value;
mod enum_impl;
mod field;
+mod options_attr;
mod struct_impl;
mod visibility;
@@ -8,9 +11,63 @@ pub fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
{
let input = syn::parse2::<syn::Item>(input).unwrap();
+ let options = get_options(&input);
+
match input {
- syn::Item::Struct(input) => struct_impl::generate(input),
- syn::Item::Enum(input) => enum_impl::generate(input),
+ syn::Item::Struct(input) => struct_impl::generate(input, options),
+ syn::Item::Enum(input) => enum_impl::generate(input, options),
_ => panic!("Invalid input"),
}
}
+
+pub fn get_options(input: &syn::Item) -> OptionsAttr
+{
+ let (attrs, generic_params) = match &input {
+ syn::Item::Struct(input) => (input.attrs.as_slice(), &input.generics.params),
+ syn::Item::Enum(input) => (input.attrs.as_slice(), &input.generics.params),
+ _ => (
+ const { &[] }.as_slice(),
+ &syn::punctuated::Punctuated::<syn::GenericParam, syn::Token![,]>::new(),
+ ),
+ };
+
+ if attrs
+ .iter()
+ .filter(|attr| attr.path().is_ident("reflection"))
+ .count()
+ > 1
+ {
+ panic!("Multiple 'reflection' attributes are not allowed");
+ }
+
+ let options = match attrs
+ .iter()
+ .find_map(|attr| {
+ if attr.path().is_ident("reflection") {
+ return Some(attr.parse_args::<OptionsAttr>());
+ }
+
+ None
+ })
+ .transpose()
+ {
+ Ok(attr) => attr.unwrap_or_default(),
+ Err(err) => {
+ panic!("{err}");
+ }
+ };
+
+ if options.impl_with_generics.is_empty() && !generic_params.is_empty() {
+ panic!(concat!(
+ "Generic types deriving Reflection must specify which ",
+ "generics to use in the generated impl(s) using the 'reflection' attribute\n",
+ "For example: #[reflection(impl_with_generics(<u32, String>, <u8, u16>))]"
+ ));
+ }
+
+ if !options.impl_with_generics.is_empty() && generic_params.is_empty() {
+ panic!("Specifying 'impl_with_generics' for non-generic types is not allowed");
+ }
+
+ options
+}
diff --git a/engine-macros/src/reflection/default_value.rs b/engine-macros/src/reflection/default_value.rs
index 567c3b1..6784129 100644
--- a/engine-macros/src/reflection/default_value.rs
+++ b/engine-macros/src/reflection/default_value.rs
@@ -2,7 +2,7 @@ use quote::quote;
pub fn gen_get_default_value_fn(
type_ident: &proc_macro2::Ident,
- type_generics: &syn::TypeGenerics,
+ generic_args: Option<&syn::AngleBracketedGenericArguments>,
) -> proc_macro2::TokenStream
{
quote! {
@@ -36,7 +36,7 @@ pub fn gen_get_default_value_fn(
}
}
- (&SpecializationTarget::<#type_ident #type_generics>(std::marker::PhantomData))
+ (&SpecializationTarget::<#type_ident #generic_args>(std::marker::PhantomData))
.default_value_fn()
}
}
diff --git a/engine-macros/src/reflection/enum_impl.rs b/engine-macros/src/reflection/enum_impl.rs
index 5dbb176..0ce2562 100644
--- a/engine-macros/src/reflection/enum_impl.rs
+++ b/engine-macros/src/reflection/enum_impl.rs
@@ -1,37 +1,20 @@
use quote::{format_ident, quote, ToTokens};
+use syn::ItemEnum;
use crate::reflection::default_value::gen_get_default_value_fn;
use crate::reflection::field::{generate as generate_field, ReflectionFieldGenOptions};
-use crate::util::{find_engine_crate_path, syn_path, SynPathExt};
+use crate::reflection::options_attr::OptionsAttr;
+use crate::util::find_engine_crate_path;
-const PRIMITIVE_REPRS: &[&'static str] = &[
- "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128",
- "isize",
-];
-
-pub fn generate(input: syn::ItemEnum) -> proc_macro2::TokenStream
+pub fn generate(input: syn::ItemEnum, options: OptionsAttr) -> proc_macro2::TokenStream
{
let engine_crate_path = find_engine_crate_path().unwrap();
- let input_ident = &input.ident;
-
- let mut generics = input.generics;
-
- for type_param in generics.type_params_mut() {
- type_param
- .bounds
- .push(syn::TypeParamBound::Trait(syn::TraitBound {
- paren_token: None,
- modifier: syn::TraitBoundModifier::None,
- lifetimes: None,
- path: engine_crate_path.join(syn_path!(reflection::Reflection)),
- }));
- }
-
- let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
-
- let variant_lookup_match_arms =
- input.variants.iter().enumerate().map(|(index, variant)| {
+ let variant_lookup_match_arms = input
+ .variants
+ .iter()
+ .enumerate()
+ .map(|(index, variant)| {
let variant_ident = &variant.ident;
let pattern = match variant.fields {
@@ -43,178 +26,74 @@ pub fn generate(input: syn::ItemEnum) -> proc_macro2::TokenStream
quote! {
#pattern => &enum_reflection.variants[#index]
}
- });
+ })
+ .collect::<Vec<_>>();
let is_unit_only = input
.variants
.iter()
.all(|variant| matches!(variant.fields, syn::Fields::Unit));
- let mod_name = format_ident!("__engine_private_{input_ident}");
-
- let mut has_c_repr = false;
-
- let mut primitive_repr: Option<proc_macro2::Ident> = None;
-
- for attr in &input.attrs {
- let syn::Meta::List(attr_meta) = &attr.meta else {
- continue;
- };
-
- if !attr_meta.path.is_ident("repr") {
- continue;
- }
-
- attr_meta
- .parse_nested_meta(|nested_meta| {
- let Some(meta_ident) = nested_meta.path.get_ident() else {
- return Ok(());
- };
-
- if meta_ident == "C" {
- has_c_repr = true;
- } else if PRIMITIVE_REPRS.contains(&meta_ident.to_string().as_str()) {
- primitive_repr = Some(meta_ident.clone());
- } else {
- return Err(nested_meta.error(concat!(
- "Unsupported representation. ",
- "Only C and primitive representations are allowed"
- )));
- }
+ let mod_name = format_ident!("__engine_private_{}", input.ident);
- Ok(())
- })
- .unwrap();
- }
+ let reprs = get_reprs(&input);
- if !has_c_repr && primitive_repr.is_none() {
+ if !reprs.has_c_repr && reprs.primitive_repr.is_none() {
panic!("Enums must have a C or primitive representation to derive Reflection");
}
- let mod_content = if is_unit_only {
- quote! {}
- } else {
- let variant_fields_structs =
- input.variants.iter().map(|variant| {
- let fields = variant.fields.iter().map(|field| {
- let field_type = &field.ty;
-
- if let Some(field_ident) = &field.ident {
- quote! { pub #field_ident: #field_type }
- } else {
- quote! { pub #field_type }
- }
- });
-
- let ident = format_ident!("VariantFields{}", variant.ident);
-
- let generics_phantom_data_elems = generics.params.iter().filter_map(
- |generic_param| match generic_param {
- syn::GenericParam::Type(type_param) => {
- Some(type_param.ident.clone().into_token_stream())
- }
- syn::GenericParam::Lifetime(_) => {
- unimplemented!();
- }
- syn::GenericParam::Const(_const_param) => None,
- },
- );
-
- let generics_phantom_data = quote! {
- std::marker::PhantomData<(#(#generics_phantom_data_elems),*)>
- };
-
- match variant.fields {
- syn::Fields::Named(_) => quote! {
- #[repr(C)]
- pub struct #ident #impl_generics
- #where_clause
- {
- #(#fields,)*
- _pd: #generics_phantom_data
- }
- },
- syn::Fields::Unnamed(_) => quote! {
- #[repr(C)]
- pub struct #ident #impl_generics (
- #(#fields,)*
- #generics_phantom_data
- )
- #where_clause;
- },
- syn::Fields::Unit => quote! {
- #[repr(C)]
- pub struct #ident #impl_generics (#generics_phantom_data)
- #where_clause;
- },
- }
- });
-
- let fields_union_fields = input.variants.iter().map(|variant| {
- let variant_ident = &variant.ident;
-
- let variant_fields_struct_ident =
- format_ident!("VariantFields{}", variant.ident);
-
- quote! {
- pub #variant_ident:
- std::mem::ManuallyDrop<#variant_fields_struct_ident #type_generics>
- }
- });
-
- let discriminant_enum_variants = input.variants.iter().map(|variant| {
- let variant_ident = &variant.ident;
-
- if let Some((_, discriminant)) = &variant.discriminant {
- quote! { #variant_ident = #discriminant }
- } else {
- quote! { #variant_ident }
- }
- });
-
- // If the enum has both a C & primitive repr, the primitive repr must be used
- let discriminant_enum_repr = if let Some(primitive_repr) = primitive_repr {
- quote! { #primitive_repr }
- } else if has_c_repr {
- quote! { C }
+ let mod_content = generate_mod_content(&input, is_unit_only, &reprs);
+
+ let impls: &mut dyn Iterator<Item = proc_macro2::TokenStream> =
+ if input.generics.params.is_empty() {
+ &mut [generate_impls(
+ &input,
+ None,
+ is_unit_only,
+ &variant_lookup_match_arms,
+ &mod_name,
+ &engine_crate_path,
+ )]
+ .into_iter()
} else {
- unreachable!();
+ &mut options.impl_with_generics.iter().map(|generic_args| {
+ generate_impls(
+ &input,
+ Some(generic_args),
+ is_unit_only,
+ &variant_lookup_match_arms,
+ &mod_name,
+ &engine_crate_path,
+ )
+ })
};
- quote! {
- #![allow(non_snake_case, dead_code)]
-
- use super::*;
-
- #[repr(C)]
- pub struct Equivalent #impl_generics
- #where_clause
- {
- pub tag: Discriminant,
- pub payload: Fields #type_generics
- }
-
- #[repr(#discriminant_enum_repr)]
- pub enum Discriminant
- {
- #(#discriminant_enum_variants),*
- }
+ quote! {
+ #[doc(hidden)]
+ #[allow(non_snake_case)]
+ mod #mod_name {
+ #mod_content
+ }
- #[repr(C)]
- pub union Fields #impl_generics
- #where_clause
- {
- #(#fields_union_fields),*
- }
+ #(#impls)*
+ }
+}
- #(#variant_fields_structs)*
- }
- };
+fn generate_impls(
+ input: &syn::ItemEnum,
+ generic_args: Option<&syn::AngleBracketedGenericArguments>,
+ is_unit_only: bool,
+ variant_lookup_match_arms: &[proc_macro2::TokenStream],
+ mod_name: &proc_macro2::Ident,
+ engine_crate_path: &syn::Path,
+) -> proc_macro2::TokenStream
+{
+ let get_default_value_fn = gen_get_default_value_fn(&input.ident, generic_args);
let variants = generate_variants(
- input_ident,
+ &input.ident,
&input.variants,
- &generics,
+ generic_args,
is_unit_only,
&engine_crate_path,
);
@@ -228,28 +107,46 @@ pub fn generate(input: syn::ItemEnum) -> proc_macro2::TokenStream
Some(#engine_crate_path::reflection::EnumTaggedUnion {
discriminant_layout: std::alloc::Layout::new::<#mod_name::Discriminant>(),
discriminant_byte_offset:
- std::mem::offset_of!(#mod_name::Equivalent #type_generics, tag),
+ std::mem::offset_of!(#mod_name::Equivalent #generic_args, tag),
fields_layout:
- std::alloc::Layout::new::<#mod_name::Fields #type_generics>(),
+ std::alloc::Layout::new::<#mod_name::Fields #generic_args>(),
fields_byte_offset:
- std::mem::offset_of!(#mod_name::Equivalent #type_generics, payload),
+ std::mem::offset_of!(#mod_name::Equivalent #generic_args, payload),
})
}
};
- let get_default_value_fn = gen_get_default_value_fn(&input_ident, &type_generics);
+ let generics_type_aliases = input
+ .generics
+ .type_params()
+ .zip(
+ generic_args
+ .iter()
+ .map(|generic_args| &generic_args.args)
+ .flatten()
+ .flat_map(|generic_arg| match generic_arg {
+ syn::GenericArgument::Type(ty) => Some(ty),
+ _ => None,
+ }),
+ )
+ .map(|(type_param, generic_arg_type)| {
+ let type_param_ident = &type_param.ident;
- quote! {
- #[doc(hidden)]
- mod #mod_name {
- #mod_content
- }
+ quote! {
+ type #type_param_ident = #generic_arg_type;
+ }
+ });
+
+ let input_ident = &input.ident;
- unsafe impl #impl_generics #engine_crate_path::reflection::Reflection for
- #input_ident #type_generics #where_clause
+ quote! {
+ unsafe impl #engine_crate_path::reflection::Reflection for
+ #input_ident #generic_args
{
const TYPE_REFLECTION: &#engine_crate_path::reflection::Type =
&const {
+ #(#generics_type_aliases)*
+
#engine_crate_path::reflection::Type::Enum(
#engine_crate_path::reflection::Enum {
variants: &[#(#variants),*],
@@ -263,8 +160,8 @@ pub fn generate(input: syn::ItemEnum) -> proc_macro2::TokenStream
};
}
- unsafe impl #impl_generics #engine_crate_path::reflection::EnumReflectionExt for
- #input_ident #type_generics #where_clause
+ unsafe impl #engine_crate_path::reflection::EnumReflectionExt for
+ #input_ident #generic_args
{
fn get_variant_reflection(&self)
-> &'static #engine_crate_path::reflection::EnumVariant
@@ -286,15 +183,13 @@ pub fn generate(input: syn::ItemEnum) -> proc_macro2::TokenStream
fn generate_variants<'a>(
input_ident: &proc_macro2::Ident,
input_variants: &'a syn::punctuated::Punctuated<syn::Variant, syn::Token![,]>,
- input_generics: &'a syn::Generics,
+ generic_args: Option<&'a syn::AngleBracketedGenericArguments>,
is_unit_only: bool,
engine_crate_path: &'a syn::Path,
) -> impl Iterator<Item = proc_macro2::TokenStream> + use<'a>
{
let mod_name = format_ident!("__engine_private_{input_ident}");
- let (_, type_generics, _) = input_generics.split_for_impl();
-
input_variants.iter().map(move |variant| {
let variant_name =
syn::LitStr::new(&variant.ident.to_string(), variant.ident.span());
@@ -321,12 +216,12 @@ fn generate_variants<'a>(
if let Some(field_ident) = &field.ident {
quote! {
std::mem::offset_of!(
- #mod_name::Equivalent #type_generics,
+ #mod_name::Equivalent #generic_args,
payload.#variant_ident
) +
std::mem::offset_of!(
#mod_name::#variant_fields_struct_ident
- #type_generics,
+ #generic_args,
#field_ident
)
}
@@ -334,9 +229,6 @@ fn generate_variants<'a>(
unreachable!();
}
},
- type_reflection_optional: input_generics
- .params
- .is_empty(),
},
)
},
@@ -371,20 +263,17 @@ fn generate_variants<'a>(
quote! {
std::mem::offset_of!(
- #mod_name::Equivalent #type_generics,
+ #mod_name::Equivalent #generic_args,
payload.#variant_ident
) +
std::mem::offset_of!(
#mod_name::#variant_fields_struct_ident
- #type_generics,
+ #generic_args,
#field_index
)
}
}
},
- type_reflection_optional: input_generics
- .params
- .is_empty(),
},
)
},
@@ -449,3 +338,181 @@ fn generate_variants<'a>(
}
})
}
+
+fn generate_mod_content(
+ input: &syn::ItemEnum,
+ is_unit_only: bool,
+ reprs: &Reprs,
+) -> proc_macro2::TokenStream
+{
+ if is_unit_only {
+ return quote! {};
+ }
+
+ let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl();
+
+ let variant_fields_structs = input.variants.iter().map(|variant| {
+ let fields = variant.fields.iter().map(|field| {
+ let field_type = &field.ty;
+
+ if let Some(field_ident) = &field.ident {
+ quote! { pub #field_ident: #field_type }
+ } else {
+ quote! { pub #field_type }
+ }
+ });
+
+ let ident = format_ident!("VariantFields{}", variant.ident);
+
+ let generics_phantom_data_elems = input.generics.params.iter().filter_map(
+ |generic_param| match generic_param {
+ syn::GenericParam::Type(type_param) => {
+ Some(type_param.ident.clone().into_token_stream())
+ }
+ syn::GenericParam::Lifetime(_) => {
+ unimplemented!();
+ }
+ syn::GenericParam::Const(_const_param) => None,
+ },
+ );
+
+ let generics_phantom_data = quote! {
+ std::marker::PhantomData<(#(#generics_phantom_data_elems),*)>
+ };
+
+ match variant.fields {
+ syn::Fields::Named(_) => quote! {
+ #[repr(C)]
+ pub struct #ident #impl_generics
+ #where_clause
+ {
+ #(#fields,)*
+ _pd: #generics_phantom_data
+ }
+ },
+ syn::Fields::Unnamed(_) => quote! {
+ #[repr(C)]
+ pub struct #ident #impl_generics (
+ #(#fields,)*
+ #generics_phantom_data
+ )
+ #where_clause;
+ },
+ syn::Fields::Unit => quote! {
+ #[repr(C)]
+ pub struct #ident #impl_generics (#generics_phantom_data)
+ #where_clause;
+ },
+ }
+ });
+
+ let fields_union_fields = input.variants.iter().map(|variant| {
+ let variant_ident = &variant.ident;
+
+ let variant_fields_struct_ident = format_ident!("VariantFields{}", variant.ident);
+
+ quote! {
+ pub #variant_ident:
+ std::mem::ManuallyDrop<#variant_fields_struct_ident #type_generics>
+ }
+ });
+
+ let discriminant_enum_variants = input.variants.iter().map(|variant| {
+ let variant_ident = &variant.ident;
+
+ if let Some((_, discriminant)) = &variant.discriminant {
+ quote! { #variant_ident = #discriminant }
+ } else {
+ quote! { #variant_ident }
+ }
+ });
+
+ // If the enum has both a C & primitive repr, the primitive repr must be used
+ let discriminant_enum_repr = if let Some(primitive_repr) = &reprs.primitive_repr {
+ quote! { #primitive_repr }
+ } else if reprs.has_c_repr {
+ quote! { C }
+ } else {
+ unreachable!();
+ };
+
+ quote! {
+ #![allow(non_snake_case, dead_code)]
+
+ use super::*;
+
+ #[repr(C)]
+ pub struct Equivalent #impl_generics
+ #where_clause
+ {
+ pub tag: Discriminant,
+ pub payload: Fields #type_generics
+ }
+
+ #[repr(#discriminant_enum_repr)]
+ pub enum Discriminant
+ {
+ #(#discriminant_enum_variants),*
+ }
+
+ #[repr(C)]
+ pub union Fields #impl_generics
+ #where_clause
+ {
+ #(#fields_union_fields),*
+ }
+
+ #(#variant_fields_structs)*
+ }
+}
+
+fn get_reprs(input: &ItemEnum) -> Reprs
+{
+ let mut has_c_repr = false;
+ let mut primitive_repr: Option<proc_macro2::Ident> = None;
+
+ for attr in &input.attrs {
+ let syn::Meta::List(attr_meta) = &attr.meta else {
+ continue;
+ };
+
+ if !attr_meta.path.is_ident("repr") {
+ continue;
+ }
+
+ attr_meta
+ .parse_nested_meta(|nested_meta| {
+ let Some(meta_ident) = nested_meta.path.get_ident() else {
+ return Ok(());
+ };
+
+ if meta_ident == "C" {
+ has_c_repr = true;
+ } else if PRIMITIVE_REPRS.contains(&meta_ident.to_string().as_str()) {
+ primitive_repr = Some(meta_ident.clone());
+ } else {
+ return Err(nested_meta.error(concat!(
+ "Unsupported representation. ",
+ "Only C and primitive representations are allowed"
+ )));
+ }
+
+ Ok(())
+ })
+ .unwrap();
+ }
+
+ Reprs { has_c_repr, primitive_repr }
+}
+
+#[derive(Debug)]
+struct Reprs
+{
+ has_c_repr: bool,
+ primitive_repr: Option<proc_macro2::Ident>,
+}
+
+const PRIMITIVE_REPRS: &[&'static str] = &[
+ "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128",
+ "isize",
+];
diff --git a/engine-macros/src/reflection/field.rs b/engine-macros/src/reflection/field.rs
index 95671c3..7c50f47 100644
--- a/engine-macros/src/reflection/field.rs
+++ b/engine-macros/src/reflection/field.rs
@@ -6,7 +6,6 @@ pub struct ReflectionFieldGenOptions<'a>
{
pub field_vis_override: Option<syn::Visibility>,
pub gen_get_byte_offset: &'a dyn Fn(&syn::Field) -> proc_macro2::TokenStream,
- pub type_reflection_optional: bool,
}
pub fn generate(
@@ -34,11 +33,8 @@ pub fn generate(
let field_reflection_vis = generate_visibility(field_vis, &engine_crate_path);
- let get_type_fn_body = if options.type_reflection_optional {
- gen_get_optional_type_reflection(field_type, engine_crate_path)
- } else {
- gen_get_mandatory_type_reflection(field_type, engine_crate_path)
- };
+ let get_type_fn_body =
+ { gen_get_optional_type_reflection(field_type, engine_crate_path) };
quote! {
#engine_crate_path::reflection::Field {
@@ -102,19 +98,3 @@ fn gen_get_optional_type_reflection(
.field_type_reflection()
}
}
-
-fn gen_get_mandatory_type_reflection(
- field_type: &syn::Type,
- engine_crate_path: &syn::Path,
-) -> proc_macro2::TokenStream
-{
- quote! {
- fn field_type_reflection<T: #engine_crate_path::reflection::Reflection>()
- -> &'static #engine_crate_path::reflection::Type
- {
- T::type_reflection()
- }
-
- Some(field_type_reflection::<#field_type>())
- }
-}
diff --git a/engine-macros/src/reflection/options_attr.rs b/engine-macros/src/reflection/options_attr.rs
new file mode 100644
index 0000000..231b969
--- /dev/null
+++ b/engine-macros/src/reflection/options_attr.rs
@@ -0,0 +1,37 @@
+#[derive(Debug, Default)]
+pub struct OptionsAttr
+{
+ pub impl_with_generics:
+ syn::punctuated::Punctuated<syn::AngleBracketedGenericArguments, syn::Token![,]>,
+}
+
+impl syn::parse::Parse for OptionsAttr
+{
+ fn parse(input: syn::parse::ParseStream) -> syn::Result<Self>
+ {
+ let params =
+ input.parse_terminated(|param| param.parse::<syn::Meta>(), syn::Token![,])?;
+
+ let mut impl_with_generics = syn::punctuated::Punctuated::new();
+
+ for param in params {
+ let param = param.require_list()?;
+
+ if !param.path.is_ident("impl_with_generics") {
+ return Err(syn::Error::new_spanned(
+ param,
+ "Unrecognized attribute parameter",
+ ));
+ }
+
+ impl_with_generics = param.parse_args_with(
+ syn::punctuated::Punctuated::<
+ syn::AngleBracketedGenericArguments,
+ syn::Token![,],
+ >::parse_terminated,
+ )?;
+ }
+
+ Ok(Self { impl_with_generics })
+ }
+}
diff --git a/engine-macros/src/reflection/struct_impl.rs b/engine-macros/src/reflection/struct_impl.rs
index ce1086b..58046fe 100644
--- a/engine-macros/src/reflection/struct_impl.rs
+++ b/engine-macros/src/reflection/struct_impl.rs
@@ -2,62 +2,85 @@ use quote::quote;
use crate::reflection::default_value::gen_get_default_value_fn;
use crate::reflection::field::{generate as generate_field, ReflectionFieldGenOptions};
-use crate::util::{find_engine_crate_path, syn_path, SynPathExt};
+use crate::reflection::options_attr::OptionsAttr;
+use crate::util::find_engine_crate_path;
-pub fn generate(input: syn::ItemStruct) -> proc_macro2::TokenStream
+pub fn generate(input: syn::ItemStruct, options: OptionsAttr)
+ -> proc_macro2::TokenStream
{
let engine_crate_path = find_engine_crate_path().unwrap();
- let input_ident = input.ident;
-
- let type_reflection_optional = input.generics.params.is_empty();
+ if input.generics.params.is_empty() {
+ return gen_impl(&input, None, &engine_crate_path);
+ }
- let mut generics = input.generics;
+ let impls = options
+ .impl_with_generics
+ .into_iter()
+ .map(|generic_args| gen_impl(&input, Some(&generic_args), &engine_crate_path));
- for type_param in generics.type_params_mut() {
- type_param
- .bounds
- .push(syn::TypeParamBound::Trait(syn::TraitBound {
- paren_token: None,
- modifier: syn::TraitBoundModifier::None,
- lifetimes: None,
- path: engine_crate_path.join(syn_path!(reflection::Reflection)),
- }));
+ quote! {
+ #(#impls)*
}
+}
- let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
-
- let fields = input
- .fields
- .into_iter()
- .enumerate()
- .map(|(field_index, field)| {
- generate_field(
- &field,
- field_index,
- &engine_crate_path,
- ReflectionFieldGenOptions {
- field_vis_override: None,
- gen_get_byte_offset: &|field| {
- if let Some(field_ident) = &field.ident {
- quote! { std::mem::offset_of!(Self, #field_ident) }
- } else {
- quote! { std::mem::offset_of!(Self, #field_index) }
- }
- },
- type_reflection_optional,
+fn gen_impl(
+ input: &syn::ItemStruct,
+ generic_args: Option<&syn::AngleBracketedGenericArguments>,
+ engine_crate_path: &syn::Path,
+) -> proc_macro2::TokenStream
+{
+ let fields = input.fields.iter().enumerate().map(|(field_index, field)| {
+ generate_field(
+ &field,
+ field_index,
+ &engine_crate_path,
+ ReflectionFieldGenOptions {
+ field_vis_override: None,
+ gen_get_byte_offset: &|field| {
+ if let Some(field_ident) = &field.ident {
+ quote! { std::mem::offset_of!(Self, #field_ident) }
+ } else {
+ quote! { std::mem::offset_of!(Self, #field_index) }
+ }
},
- )
- });
+ },
+ )
+ });
+
+ let get_default_value_fn = gen_get_default_value_fn(&input.ident, generic_args);
+
+ let input_ident = &input.ident;
- let get_default_value_fn = gen_get_default_value_fn(&input_ident, &type_generics);
+ let generics_type_aliases = input
+ .generics
+ .type_params()
+ .zip(
+ generic_args
+ .iter()
+ .map(|generic_args| &generic_args.args)
+ .flatten()
+ .flat_map(|generic_arg| match generic_arg {
+ syn::GenericArgument::Type(ty) => Some(ty),
+ _ => None,
+ }),
+ )
+ .map(|(type_param, generic_arg_type)| {
+ let type_param_ident = &type_param.ident;
+
+ quote! {
+ type #type_param_ident = #generic_arg_type;
+ }
+ });
quote! {
- unsafe impl #impl_generics #engine_crate_path::reflection::Reflection for
- #input_ident #type_generics #where_clause
+ unsafe impl #engine_crate_path::reflection::Reflection for
+ #input_ident #generic_args
{
const TYPE_REFLECTION: &#engine_crate_path::reflection::Type =
&const {
+ #(#generics_type_aliases)*
+
#engine_crate_path::reflection::Type::Struct(
#engine_crate_path::reflection::Struct {
fields: &[
diff --git a/engine-macros/src/util.rs b/engine-macros/src/util.rs
index 327c517..515b066 100644
--- a/engine-macros/src/util.rs
+++ b/engine-macros/src/util.rs
@@ -7,8 +7,8 @@ macro_rules! syn_path {
::syn::Path {
leading_colon: None,
segments: ::syn::punctuated::Punctuated::from_iter([
- $crate::util::syn_path_segment!($first_segment),
- $($crate::util::syn_path_segment!($segment),)*
+ syn_path_segment!($first_segment),
+ $(syn_path_segment!($segment),)*
])
}
};
@@ -26,9 +26,6 @@ macro_rules! syn_path_segment {
};
}
-pub(crate) use syn_path;
-pub(crate) use syn_path_segment;
-
pub fn find_engine_crate_path() -> Option<syn::Path>
{
let cargo_crate_name = std::env::var("CARGO_CRATE_NAME").ok()?;
@@ -66,20 +63,3 @@ pub fn syn_path_to_string(path: &syn::Path) -> String
output
}
-
-pub trait SynPathExt
-{
- fn join(&self, tail: Self) -> Self;
-}
-
-impl SynPathExt for syn::Path
-{
- fn join(&self, tail: Self) -> Self
- {
- let mut new = self.clone();
-
- new.segments.extend(tail.segments);
-
- new
- }
-}