1use core::fmt;
2use indexmap::IndexSet;
3use proc_macro2::Span;
4
5use crate::{
6 container_attributes::{ContainerAttributes, FromReflectAttrs, TypePathAttrs},
7 field_attributes::FieldAttributes,
8 remote::RemoteType,
9 serialization::SerializationDataDef,
10 string_expr::StringExpr,
11 type_path::parse_path_no_leading_colon,
12 where_clause_options::WhereClauseOptions,
13 REFLECT_ATTRIBUTE_NAME, TYPE_NAME_ATTRIBUTE_NAME, TYPE_PATH_ATTRIBUTE_NAME,
14};
15use bevy_macro_utils::ResultSifter;
16use quote::{format_ident, quote, ToTokens};
17use syn::token::Comma;
18
19use crate::enum_utility::{EnumVariantOutputData, ReflectCloneVariantBuilder, VariantBuilder};
20use crate::field_attributes::CloneBehavior;
21use crate::generics::generate_generics;
22use bevy_macro_utils::fq_std::{FQClone, FQOption, FQResult};
23use syn::{
24 parse_str, punctuated::Punctuated, spanned::Spanned, Data, DeriveInput, Field, Fields,
25 GenericParam, Generics, Ident, LitStr, Member, Meta, Path, PathSegment, Type, TypeParam,
26 Variant,
27};
28
29pub(crate) enum ReflectDerive<'a> {
30 Struct(ReflectStruct<'a>),
31 TupleStruct(ReflectStruct<'a>),
32 UnitStruct(ReflectStruct<'a>),
33 Enum(ReflectEnum<'a>),
34 Opaque(ReflectMeta<'a>),
35}
36
37pub(crate) struct ReflectMeta<'a> {
51 attrs: ContainerAttributes,
53 type_path: ReflectTypePath<'a>,
55 remote_ty: Option<RemoteType<'a>>,
57 bevy_reflect_path: Path,
59 #[cfg(feature = "documentation")]
61 docs: crate::documentation::Documentation,
62}
63
64pub(crate) struct ReflectStruct<'a> {
78 meta: ReflectMeta<'a>,
79 serialization_data: Option<SerializationDataDef>,
80 fields: Vec<StructField<'a>>,
81}
82
83pub(crate) struct ReflectEnum<'a> {
97 meta: ReflectMeta<'a>,
98 variants: Vec<EnumVariant<'a>>,
99}
100
101#[derive(Clone)]
103pub(crate) struct StructField<'a> {
104 pub data: &'a Field,
106 pub attrs: FieldAttributes,
108 pub declaration_index: usize,
110 pub reflection_index: Option<usize>,
117 #[cfg(feature = "documentation")]
119 pub doc: crate::documentation::Documentation,
120}
121
122pub(crate) struct EnumVariant<'a> {
124 pub data: &'a Variant,
126 pub fields: EnumVariantFields<'a>,
128 pub attrs: FieldAttributes,
130 #[cfg(feature = "documentation")]
132 pub doc: crate::documentation::Documentation,
133}
134
135pub(crate) enum EnumVariantFields<'a> {
136 Named(Vec<StructField<'a>>),
137 Unnamed(Vec<StructField<'a>>),
138 Unit,
139}
140
141#[derive(Debug, Copy, Clone, PartialEq, Eq)]
143pub(crate) enum ReflectImplSource {
144 ImplRemoteType,
146 DeriveLocalType,
148 RemoteReflect,
150}
151
152#[derive(Debug, Copy, Clone, PartialEq, Eq)]
154pub(crate) enum ReflectTraitToImpl {
155 Reflect,
156 FromReflect,
157 TypePath,
158}
159
160#[derive(Debug, Copy, Clone, PartialEq, Eq)]
162pub(crate) struct ReflectProvenance {
163 pub source: ReflectImplSource,
164 pub trait_: ReflectTraitToImpl,
165}
166
167impl fmt::Display for ReflectProvenance {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 use self::{ReflectImplSource as S, ReflectTraitToImpl as T};
170 let str = match (self.source, self.trait_) {
171 (S::ImplRemoteType, T::Reflect) => "`impl_reflect`",
172 (S::DeriveLocalType, T::Reflect) => "`#[derive(Reflect)]`",
173 (S::DeriveLocalType, T::FromReflect) => "`#[derive(FromReflect)]`",
174 (S::DeriveLocalType, T::TypePath) => "`#[derive(TypePath)]`",
175 (S::RemoteReflect, T::Reflect) => "`#[reflect_remote]`",
176 (S::RemoteReflect, T::FromReflect | T::TypePath)
177 | (S::ImplRemoteType, T::FromReflect | T::TypePath) => unreachable!(),
178 };
179 f.write_str(str)
180 }
181}
182
183impl<'a> ReflectDerive<'a> {
184 pub fn from_input(
185 input: &'a DeriveInput,
186 provenance: ReflectProvenance,
187 ) -> Result<Self, syn::Error> {
188 let mut container_attributes = ContainerAttributes::default();
189 let mut custom_path: Option<Path> = None;
191 let mut custom_type_name: Option<Ident> = None;
193
194 #[cfg(feature = "documentation")]
195 let mut doc = crate::documentation::Documentation::default();
196
197 for attribute in &input.attrs {
198 match &attribute.meta {
199 Meta::List(meta_list) if meta_list.path.is_ident(REFLECT_ATTRIBUTE_NAME) => {
200 container_attributes.parse_meta_list(meta_list, provenance.trait_)?;
201 }
202 Meta::NameValue(pair) if pair.path.is_ident(TYPE_PATH_ATTRIBUTE_NAME) => {
203 let syn::Expr::Lit(syn::ExprLit {
204 lit: syn::Lit::Str(lit),
205 ..
206 }) = &pair.value
207 else {
208 return Err(syn::Error::new(
209 pair.span(),
210 format_args!("`#[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"]` must be a string literal"),
211 ));
212 };
213
214 custom_path = Some(syn::parse::Parser::parse_str(
215 parse_path_no_leading_colon,
216 &lit.value(),
217 )?);
218 }
219 Meta::NameValue(pair) if pair.path.is_ident(TYPE_NAME_ATTRIBUTE_NAME) => {
220 let syn::Expr::Lit(syn::ExprLit {
221 lit: syn::Lit::Str(lit),
222 ..
223 }) = &pair.value
224 else {
225 return Err(syn::Error::new(
226 pair.span(),
227 format_args!("`#[{TYPE_NAME_ATTRIBUTE_NAME} = \"...\"]` must be a string literal"),
228 ));
229 };
230
231 custom_type_name = Some(parse_str(&lit.value())?);
232 }
233 #[cfg(feature = "documentation")]
234 Meta::NameValue(pair) if pair.path.is_ident("doc") => {
235 if let syn::Expr::Lit(syn::ExprLit {
236 lit: syn::Lit::Str(lit),
237 ..
238 }) = &pair.value
239 {
240 doc.push(lit.value());
241 }
242 }
243 _ => continue,
244 }
245 }
246 match (&mut custom_path, custom_type_name) {
247 (Some(path), custom_type_name) => {
248 let ident = custom_type_name.unwrap_or_else(|| input.ident.clone());
249 path.segments.push(PathSegment::from(ident));
250 }
251 (None, Some(name)) => {
252 return Err(syn::Error::new(
253 name.span(),
254 format!("cannot use `#[{TYPE_NAME_ATTRIBUTE_NAME} = \"...\"]` without a `#[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"]` attribute."),
255 ));
256 }
257 _ => (),
258 }
259
260 let type_path = ReflectTypePath::Internal {
261 ident: &input.ident,
262 custom_path,
263 generics: &input.generics,
264 };
265
266 let meta = ReflectMeta::new(type_path, container_attributes);
267
268 if provenance.source == ReflectImplSource::ImplRemoteType
269 && meta.type_path_attrs().should_auto_derive()
270 && !meta.type_path().has_custom_path()
271 {
272 return Err(syn::Error::new(
273 meta.type_path().span(),
274 format!("a #[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"] attribute must be specified when using {provenance}"),
275 ));
276 }
277
278 #[cfg(feature = "documentation")]
279 let meta = meta.with_docs(doc);
280
281 if meta.attrs().is_opaque() {
282 return Ok(Self::Opaque(meta));
283 }
284
285 match &input.data {
286 Data::Struct(data) => {
287 let fields = Self::collect_struct_fields(&data.fields)?;
288 let serialization_data =
289 SerializationDataDef::new(&fields, &meta.bevy_reflect_path)?;
290 let reflect_struct = ReflectStruct {
291 meta,
292 serialization_data,
293 fields,
294 };
295
296 match data.fields {
297 Fields::Named(..) => Ok(Self::Struct(reflect_struct)),
298 Fields::Unnamed(..) => Ok(Self::TupleStruct(reflect_struct)),
299 Fields::Unit => Ok(Self::UnitStruct(reflect_struct)),
300 }
301 }
302 Data::Enum(data) => {
303 let variants = Self::collect_enum_variants(&data.variants)?;
304
305 let reflect_enum = ReflectEnum { meta, variants };
306 Ok(Self::Enum(reflect_enum))
307 }
308 Data::Union(..) => Err(syn::Error::new(
309 input.span(),
310 "reflection not supported for unions",
311 )),
312 }
313 }
314
315 pub fn set_remote(&mut self, remote_ty: Option<RemoteType<'a>>) {
321 match self {
322 Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {
323 data.meta.remote_ty = remote_ty;
324 }
325 Self::Enum(data) => {
326 data.meta.remote_ty = remote_ty;
327 }
328 Self::Opaque(meta) => {
329 meta.remote_ty = remote_ty;
330 }
331 }
332 }
333
334 pub fn remote_ty(&self) -> Option<RemoteType<'_>> {
336 match self {
337 Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {
338 data.meta.remote_ty()
339 }
340 Self::Enum(data) => data.meta.remote_ty(),
341 Self::Opaque(meta) => meta.remote_ty(),
342 }
343 }
344
345 pub fn meta(&self) -> &ReflectMeta<'_> {
347 match self {
348 Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => data.meta(),
349 Self::Enum(data) => data.meta(),
350 Self::Opaque(meta) => meta,
351 }
352 }
353
354 pub fn where_clause_options(&self) -> WhereClauseOptions<'_, '_> {
355 match self {
356 Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {
357 data.where_clause_options()
358 }
359 Self::Enum(data) => data.where_clause_options(),
360 Self::Opaque(meta) => WhereClauseOptions::new(meta),
361 }
362 }
363
364 fn collect_struct_fields(fields: &'a Fields) -> Result<Vec<StructField<'a>>, syn::Error> {
365 let mut active_index = 0;
366 let sifter: ResultSifter<StructField<'a>> = fields
367 .iter()
368 .enumerate()
369 .map(
370 |(declaration_index, field)| -> Result<StructField, syn::Error> {
371 let attrs = FieldAttributes::parse_attributes(&field.attrs)?;
372
373 let reflection_index = if attrs.ignore.is_ignored() {
374 None
375 } else {
376 active_index += 1;
377 Some(active_index - 1)
378 };
379
380 Ok(StructField {
381 declaration_index,
382 reflection_index,
383 attrs,
384 data: field,
385 #[cfg(feature = "documentation")]
386 doc: crate::documentation::Documentation::from_attributes(&field.attrs),
387 })
388 },
389 )
390 .fold(ResultSifter::default(), ResultSifter::fold);
391
392 sifter.finish()
393 }
394
395 fn collect_enum_variants(
396 variants: &'a Punctuated<Variant, Comma>,
397 ) -> Result<Vec<EnumVariant<'a>>, syn::Error> {
398 let sifter: ResultSifter<EnumVariant<'a>> = variants
399 .iter()
400 .map(|variant| -> Result<EnumVariant, syn::Error> {
401 let fields = Self::collect_struct_fields(&variant.fields)?;
402
403 let fields = match variant.fields {
404 Fields::Named(..) => EnumVariantFields::Named(fields),
405 Fields::Unnamed(..) => EnumVariantFields::Unnamed(fields),
406 Fields::Unit => EnumVariantFields::Unit,
407 };
408 Ok(EnumVariant {
409 fields,
410 attrs: FieldAttributes::parse_attributes(&variant.attrs)?,
411 data: variant,
412 #[cfg(feature = "documentation")]
413 doc: crate::documentation::Documentation::from_attributes(&variant.attrs),
414 })
415 })
416 .fold(ResultSifter::default(), ResultSifter::fold);
417
418 sifter.finish()
419 }
420}
421
422impl<'a> ReflectMeta<'a> {
423 pub fn new(type_path: ReflectTypePath<'a>, attrs: ContainerAttributes) -> Self {
424 Self {
425 attrs,
426 type_path,
427 remote_ty: None,
428 bevy_reflect_path: crate::meta::get_bevy_reflect_path(),
429 #[cfg(feature = "documentation")]
430 docs: Default::default(),
431 }
432 }
433
434 #[cfg(feature = "documentation")]
436 pub fn with_docs(self, docs: crate::documentation::Documentation) -> Self {
437 Self { docs, ..self }
438 }
439
440 pub fn attrs(&self) -> &ContainerAttributes {
442 &self.attrs
443 }
444
445 #[expect(
447 clippy::wrong_self_convention,
448 reason = "Method returns `FromReflectAttrs`, does not actually convert data."
449 )]
450 pub fn from_reflect(&self) -> &FromReflectAttrs {
451 self.attrs.from_reflect_attrs()
452 }
453
454 pub fn type_path_attrs(&self) -> &TypePathAttrs {
456 self.attrs.type_path_attrs()
457 }
458
459 pub fn type_path(&self) -> &ReflectTypePath<'a> {
461 &self.type_path
462 }
463
464 pub fn remote_ty(&self) -> Option<RemoteType<'_>> {
466 self.remote_ty
467 }
468
469 pub fn is_remote_wrapper(&self) -> bool {
471 self.remote_ty.is_some()
472 }
473
474 pub fn bevy_reflect_path(&self) -> &Path {
476 &self.bevy_reflect_path
477 }
478
479 pub fn get_type_registration(
481 &self,
482 where_clause_options: &WhereClauseOptions,
483 ) -> proc_macro2::TokenStream {
484 crate::registration::impl_get_type_registration(
485 where_clause_options,
486 None,
487 Option::<core::iter::Empty<&Type>>::None,
488 )
489 }
490
491 #[cfg(feature = "documentation")]
493 pub fn doc(&self) -> &crate::documentation::Documentation {
494 &self.docs
495 }
496}
497
498impl<'a> StructField<'a> {
499 pub fn to_info_tokens(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {
501 let name = match &self.data.ident {
502 Some(ident) => ident.to_string().to_token_stream(),
503 None => self.reflection_index.to_token_stream(),
504 };
505
506 let field_info = if self.data.ident.is_some() {
507 quote! {
508 #bevy_reflect_path::NamedField
509 }
510 } else {
511 quote! {
512 #bevy_reflect_path::UnnamedField
513 }
514 };
515
516 let ty = self.reflected_type();
517
518 let mut info = quote! {
519 #field_info::new::<#ty>(#name)
520 };
521
522 let custom_attributes = &self.attrs.custom_attributes;
523 if !custom_attributes.is_empty() {
524 let custom_attributes = custom_attributes.to_tokens(bevy_reflect_path);
525 info.extend(quote! {
526 .with_custom_attributes(#custom_attributes)
527 });
528 }
529
530 #[cfg(feature = "documentation")]
531 {
532 let docs = &self.doc;
533 if !docs.is_empty() {
534 info.extend(quote! {
535 .with_docs(#docs)
536 });
537 }
538 }
539
540 info
541 }
542
543 pub fn reflected_type(&self) -> &Type {
549 self.attrs.remote.as_ref().unwrap_or(&self.data.ty)
550 }
551
552 pub fn attrs(&self) -> &FieldAttributes {
553 &self.attrs
554 }
555
556 pub fn to_member(&self) -> Member {
561 match &self.data.ident {
562 Some(ident) => Member::Named(ident.clone()),
563 None => Member::Unnamed(self.declaration_index.into()),
564 }
565 }
566
567 pub fn field_id(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {
569 match &self.data.ident {
570 Some(ident) => {
571 let name = ident.to_string();
572 quote!(#bevy_reflect_path::FieldId::Named(#bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(#name)))
573 }
574 None => {
575 let index = self.declaration_index;
576 quote!(#bevy_reflect_path::FieldId::Unnamed(#index))
577 }
578 }
579 }
580}
581
582impl<'a> ReflectStruct<'a> {
583 pub fn meta(&self) -> &ReflectMeta<'a> {
585 &self.meta
586 }
587
588 pub fn serialization_data(&self) -> Option<&SerializationDataDef> {
590 self.serialization_data.as_ref()
591 }
592
593 pub fn get_type_registration(
597 &self,
598 where_clause_options: &WhereClauseOptions,
599 ) -> proc_macro2::TokenStream {
600 crate::registration::impl_get_type_registration(
601 where_clause_options,
602 self.serialization_data(),
603 Some(self.active_types().iter()),
604 )
605 }
606
607 pub fn active_types(&self) -> IndexSet<Type> {
609 self.active_fields()
611 .map(|field| field.reflected_type().clone())
612 .collect::<IndexSet<_>>()
613 }
614
615 pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
617 self.fields()
618 .iter()
619 .filter(|field| field.attrs.ignore.is_active())
620 }
621
622 pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
624 self.fields()
625 .iter()
626 .filter(|field| field.attrs.ignore.is_ignored())
627 }
628
629 pub fn fields(&self) -> &[StructField<'a>] {
631 &self.fields
632 }
633
634 pub fn where_clause_options(&self) -> WhereClauseOptions<'_, '_> {
635 WhereClauseOptions::new_with_types(self.meta(), self.active_types())
636 }
637
638 pub fn to_info_tokens(&self, is_tuple: bool) -> proc_macro2::TokenStream {
640 let bevy_reflect_path = self.meta().bevy_reflect_path();
641
642 let (info_variant, info_struct) = if is_tuple {
643 (
644 Ident::new("TupleStruct", Span::call_site()),
645 Ident::new("TupleStructInfo", Span::call_site()),
646 )
647 } else {
648 (
649 Ident::new("Struct", Span::call_site()),
650 Ident::new("StructInfo", Span::call_site()),
651 )
652 };
653
654 let field_infos = self
655 .active_fields()
656 .map(|field| field.to_info_tokens(bevy_reflect_path));
657
658 let mut info = quote! {
659 #bevy_reflect_path::#info_struct::new::<Self>(&[
660 #(#field_infos),*
661 ])
662 };
663
664 let custom_attributes = self.meta.attrs.custom_attributes();
665 if !custom_attributes.is_empty() {
666 let custom_attributes = custom_attributes.to_tokens(bevy_reflect_path);
667 info.extend(quote! {
668 .with_custom_attributes(#custom_attributes)
669 });
670 }
671
672 if let Some(generics) = generate_generics(self.meta()) {
673 info.extend(quote! {
674 .with_generics(#generics)
675 });
676 }
677
678 #[cfg(feature = "documentation")]
679 {
680 let docs = self.meta().doc();
681 if !docs.is_empty() {
682 info.extend(quote! {
683 .with_docs(#docs)
684 });
685 }
686 }
687
688 quote! {
689 #bevy_reflect_path::TypeInfo::#info_variant(#info)
690 }
691 }
692 pub fn get_clone_impl(&self) -> Option<proc_macro2::TokenStream> {
694 let bevy_reflect_path = self.meta().bevy_reflect_path();
695
696 if let container_clone @ Some(_) = self.meta().attrs().get_clone_impl(bevy_reflect_path) {
697 return container_clone;
698 }
699
700 let mut tokens = proc_macro2::TokenStream::new();
701
702 for field in self.fields().iter() {
703 let field_ty = field.reflected_type();
704 let member = field.to_member();
705 let accessor = self.access_for_field(field, false);
706
707 match &field.attrs.clone {
708 CloneBehavior::Default => {
709 let value = if field.attrs.ignore.is_ignored() {
710 let field_id = field.field_id(bevy_reflect_path);
711
712 quote! {
713 return #FQResult::Err(#bevy_reflect_path::ReflectCloneError::FieldNotCloneable {
714 field: #field_id,
715 variant: #FQOption::None,
716 container_type_path: #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(
717 <Self as #bevy_reflect_path::TypePath>::type_path()
718 )
719 })
720 }
721 } else {
722 quote! {
723 <#field_ty as #bevy_reflect_path::PartialReflect>::reflect_clone_and_take(#accessor)?
724 }
725 };
726
727 tokens.extend(quote! {
728 #member: #value,
729 });
730 }
731 CloneBehavior::Trait => {
732 tokens.extend(quote! {
733 #member: #FQClone::clone(#accessor),
734 });
735 }
736 CloneBehavior::Func(clone_fn) => {
737 tokens.extend(quote! {
738 #member: #clone_fn(#accessor),
739 });
740 }
741 }
742 }
743
744 let ctor = match self.meta.remote_ty() {
745 Some(ty) => {
746 let ty = ty.as_expr_path().ok()?.to_token_stream();
747 quote! {
748 Self(#ty {
749 #tokens
750 })
751 }
752 }
753 None => {
754 quote! {
755 Self {
756 #tokens
757 }
758 }
759 }
760 };
761
762 Some(quote! {
763 #[inline]
764 #[allow(unreachable_code, reason = "Ignored fields without a `clone` attribute will early-return with an error")]
765 fn reflect_clone(&self) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>, #bevy_reflect_path::ReflectCloneError> {
766 #FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#ctor))
767 }
768 })
769 }
770
771 pub fn access_for_field(
780 &self,
781 field: &StructField<'a>,
782 is_mutable: bool,
783 ) -> proc_macro2::TokenStream {
784 let bevy_reflect_path = self.meta().bevy_reflect_path();
785 let member = field.to_member();
786
787 let prefix_tokens = if is_mutable { quote!(&mut) } else { quote!(&) };
788
789 let accessor = if self.meta.is_remote_wrapper() {
790 quote!(self.0.#member)
791 } else {
792 quote!(self.#member)
793 };
794
795 match &field.attrs.remote {
796 Some(wrapper_ty) => {
797 let method = if is_mutable {
798 format_ident!("as_wrapper_mut")
799 } else {
800 format_ident!("as_wrapper")
801 };
802
803 quote! {
804 <#wrapper_ty as #bevy_reflect_path::ReflectRemote>::#method(#prefix_tokens #accessor)
805 }
806 }
807 None => quote!(#prefix_tokens #accessor),
808 }
809 }
810}
811
812impl<'a> ReflectEnum<'a> {
813 pub fn meta(&self) -> &ReflectMeta<'a> {
815 &self.meta
816 }
817
818 pub fn get_unit(&self, variant: &Ident) -> proc_macro2::TokenStream {
822 let name = self
823 .meta
824 .remote_ty
825 .map(|path| match path.as_expr_path() {
826 Ok(path) => path.to_token_stream(),
827 Err(err) => err.into_compile_error(),
828 })
829 .unwrap_or_else(|| self.meta.type_path().to_token_stream());
830
831 quote! {
832 #name::#variant
833 }
834 }
835
836 pub fn variants(&self) -> &[EnumVariant<'a>] {
838 &self.variants
839 }
840
841 pub fn active_types(&self) -> IndexSet<Type> {
843 self.active_fields()
845 .map(|field| field.reflected_type().clone())
846 .collect::<IndexSet<_>>()
847 }
848
849 pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
851 self.variants.iter().flat_map(EnumVariant::active_fields)
852 }
853
854 pub fn where_clause_options(&self) -> WhereClauseOptions<'_, '_> {
855 WhereClauseOptions::new_with_types(self.meta(), self.active_types())
856 }
857
858 pub fn get_type_registration(
862 &self,
863 where_clause_options: &WhereClauseOptions,
864 ) -> proc_macro2::TokenStream {
865 crate::registration::impl_get_type_registration(
866 where_clause_options,
867 None,
868 Some(self.active_types().iter()),
869 )
870 }
871
872 pub fn to_info_tokens(&self) -> proc_macro2::TokenStream {
874 let bevy_reflect_path = self.meta().bevy_reflect_path();
875
876 let variants = self
877 .variants
878 .iter()
879 .map(|variant| variant.to_info_tokens(bevy_reflect_path));
880
881 let mut info = quote! {
882 #bevy_reflect_path::EnumInfo::new::<Self>(&[
883 #(#variants),*
884 ])
885 };
886
887 let custom_attributes = self.meta.attrs.custom_attributes();
888 if !custom_attributes.is_empty() {
889 let custom_attributes = custom_attributes.to_tokens(bevy_reflect_path);
890 info.extend(quote! {
891 .with_custom_attributes(#custom_attributes)
892 });
893 }
894
895 if let Some(generics) = generate_generics(self.meta()) {
896 info.extend(quote! {
897 .with_generics(#generics)
898 });
899 }
900
901 #[cfg(feature = "documentation")]
902 {
903 let docs = self.meta().doc();
904 if !docs.is_empty() {
905 info.extend(quote! {
906 .with_docs(#docs)
907 });
908 }
909 }
910
911 quote! {
912 #bevy_reflect_path::TypeInfo::Enum(#info)
913 }
914 }
915
916 pub fn get_clone_impl(&self) -> Option<proc_macro2::TokenStream> {
918 let bevy_reflect_path = self.meta().bevy_reflect_path();
919
920 if let container_clone @ Some(_) = self.meta().attrs().get_clone_impl(bevy_reflect_path) {
921 return container_clone;
922 }
923
924 let this = Ident::new("this", Span::call_site());
925 let EnumVariantOutputData {
926 variant_patterns,
927 variant_constructors,
928 ..
929 } = ReflectCloneVariantBuilder::new(self).build(&this);
930
931 let inner = quote! {
932 match #this {
933 #(#variant_patterns => #variant_constructors),*
934 }
935 };
936
937 let body = if variant_patterns.is_empty() {
938 quote!(unreachable!())
940 } else if self.meta.is_remote_wrapper() {
941 quote! {
942 let #this = <Self as #bevy_reflect_path::ReflectRemote>::as_remote(self);
943 #FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(<Self as #bevy_reflect_path::ReflectRemote>::into_wrapper(#inner)))
944 }
945 } else {
946 quote! {
947 let #this = self;
948 #FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#inner))
949 }
950 };
951
952 Some(quote! {
953 #[inline]
954 #[allow(unreachable_code, reason = "Ignored fields without a `clone` attribute will early-return with an error")]
955 fn reflect_clone(&self) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>, #bevy_reflect_path::ReflectCloneError> {
956 #body
957 }
958 })
959 }
960}
961
962impl<'a> EnumVariant<'a> {
963 pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
965 self.fields()
966 .iter()
967 .filter(|field| field.attrs.ignore.is_active())
968 }
969
970 pub fn fields(&self) -> &[StructField<'a>] {
972 match &self.fields {
973 EnumVariantFields::Named(fields) | EnumVariantFields::Unnamed(fields) => fields,
974 EnumVariantFields::Unit => &[],
975 }
976 }
977
978 pub fn to_info_tokens(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {
980 let variant_name = &self.data.ident.to_string();
981
982 let (info_variant, info_struct) = match &self.fields {
983 EnumVariantFields::Unit => (
984 Ident::new("Unit", Span::call_site()),
985 Ident::new("UnitVariantInfo", Span::call_site()),
986 ),
987 EnumVariantFields::Unnamed(..) => (
988 Ident::new("Tuple", Span::call_site()),
989 Ident::new("TupleVariantInfo", Span::call_site()),
990 ),
991 EnumVariantFields::Named(..) => (
992 Ident::new("Struct", Span::call_site()),
993 Ident::new("StructVariantInfo", Span::call_site()),
994 ),
995 };
996
997 let fields = self
998 .active_fields()
999 .map(|field| field.to_info_tokens(bevy_reflect_path));
1000
1001 let args = match &self.fields {
1002 EnumVariantFields::Unit => quote!(#variant_name),
1003 _ => {
1004 quote!( #variant_name , &[#(#fields),*] )
1005 }
1006 };
1007
1008 let mut info = quote! {
1009 #bevy_reflect_path::#info_struct::new(#args)
1010 };
1011
1012 let custom_attributes = &self.attrs.custom_attributes;
1013 if !custom_attributes.is_empty() {
1014 let custom_attributes = custom_attributes.to_tokens(bevy_reflect_path);
1015 info.extend(quote! {
1016 .with_custom_attributes(#custom_attributes)
1017 });
1018 }
1019
1020 #[cfg(feature = "documentation")]
1021 {
1022 let docs = &self.doc;
1023 if !docs.is_empty() {
1024 info.extend(quote! {
1025 .with_docs(#docs)
1026 });
1027 }
1028 }
1029
1030 quote! {
1031 #bevy_reflect_path::VariantInfo::#info_variant(#info)
1032 }
1033 }
1034}
1035
1036pub(crate) enum ReflectTypePath<'a> {
1071 Primitive(&'a Ident),
1073 External {
1077 path: &'a Path,
1078 custom_path: Option<Path>,
1079 generics: &'a Generics,
1080 },
1081 Internal {
1090 ident: &'a Ident,
1091 custom_path: Option<Path>,
1092 generics: &'a Generics,
1093 },
1094 #[expect(
1096 dead_code,
1097 reason = "Not currently used but may be useful in the future due to its generality."
1098 )]
1099 Anonymous {
1100 qualified_type: Box<Type>,
1101 long_type_path: StringExpr,
1102 short_type_path: StringExpr,
1103 },
1104}
1105
1106impl<'a> ReflectTypePath<'a> {
1107 pub fn get_ident(&self) -> Option<&Ident> {
1113 match self {
1114 Self::Internal {
1115 ident, custom_path, ..
1116 } => Some(
1117 custom_path
1118 .as_ref()
1119 .map(|path| &path.segments.last().unwrap().ident)
1120 .unwrap_or(ident),
1121 ),
1122 Self::External {
1123 path, custom_path, ..
1124 } => Some(
1125 &custom_path
1126 .as_ref()
1127 .unwrap_or(path)
1128 .segments
1129 .last()
1130 .unwrap()
1131 .ident,
1132 ),
1133 Self::Primitive(ident) => Some(ident),
1134 _ => None,
1135 }
1136 }
1137
1138 pub fn generics(&self) -> &'a Generics {
1145 const EMPTY_GENERICS: &Generics = &Generics {
1147 gt_token: None,
1148 lt_token: None,
1149 where_clause: None,
1150 params: Punctuated::new(),
1151 };
1152
1153 match self {
1154 Self::Internal { generics, .. } | Self::External { generics, .. } => generics,
1155 _ => EMPTY_GENERICS,
1156 }
1157 }
1158
1159 pub fn impl_is_generic(&self) -> bool {
1163 !self
1166 .generics()
1167 .params
1168 .iter()
1169 .all(|param| matches!(param, GenericParam::Lifetime(_)))
1170 }
1171
1172 pub fn get_path(&self) -> Option<&Path> {
1180 match self {
1181 Self::Internal { custom_path, .. } => custom_path.as_ref(),
1182 Self::External {
1183 path, custom_path, ..
1184 } => Some(custom_path.as_ref().unwrap_or(path)),
1185 _ => None,
1186 }
1187 }
1188
1189 pub fn has_custom_path(&self) -> bool {
1194 match self {
1195 Self::Internal { custom_path, .. } | Self::External { custom_path, .. } => {
1196 custom_path.is_some()
1197 }
1198 _ => false,
1199 }
1200 }
1201
1202 pub fn crate_name(&self) -> Option<StringExpr> {
1214 if let Some(path) = self.get_path() {
1215 let crate_name = &path.segments.first().unwrap().ident;
1216 return Some(StringExpr::from(crate_name));
1217 }
1218
1219 match self {
1220 Self::Internal { .. } => Some(StringExpr::Borrowed(quote! {
1221 ::core::module_path!()
1222 .split(':')
1223 .next()
1224 .unwrap()
1225 })),
1226 Self::External { .. } => unreachable!(),
1227 _ => None,
1228 }
1229 }
1230
1231 fn reduce_generics(
1237 generics: &Generics,
1238 mut ty_generic_fn: impl FnMut(&TypeParam) -> StringExpr,
1239 bevy_reflect_path: &Path,
1240 ) -> StringExpr {
1241 let mut params = generics.params.iter().filter_map(|param| match param {
1242 GenericParam::Type(type_param) => Some(ty_generic_fn(type_param)),
1243 GenericParam::Const(const_param) => {
1244 let ident = &const_param.ident;
1245 let ty = &const_param.ty;
1246
1247 Some(StringExpr::Owned(quote! {
1248 <#ty as #bevy_reflect_path::__macro_exports::alloc_utils::ToString>::to_string(&#ident)
1249 }))
1250 }
1251 GenericParam::Lifetime(_) => None,
1252 });
1253
1254 params
1255 .next()
1256 .into_iter()
1257 .chain(params.flat_map(|x| [StringExpr::from_str(", "), x]))
1258 .collect()
1259 }
1260
1261 pub fn long_type_path(&self, bevy_reflect_path: &Path) -> StringExpr {
1265 match self {
1266 Self::Primitive(ident) => StringExpr::from(ident),
1267 Self::Anonymous { long_type_path, .. } => long_type_path.clone(),
1268 Self::Internal { generics, .. } | Self::External { generics, .. } => {
1269 let ident = self.type_ident().unwrap();
1270 let module_path = self.module_path().unwrap();
1271
1272 if self.impl_is_generic() {
1273 let generics = ReflectTypePath::reduce_generics(
1274 generics,
1275 |TypeParam { ident, .. }| {
1276 StringExpr::Borrowed(quote! {
1277 <#ident as #bevy_reflect_path::TypePath>::type_path()
1278 })
1279 },
1280 bevy_reflect_path,
1281 );
1282
1283 StringExpr::from_iter([
1284 module_path,
1285 StringExpr::from_str("::"),
1286 ident,
1287 StringExpr::from_str("<"),
1288 generics,
1289 StringExpr::from_str(">"),
1290 ])
1291 } else {
1292 StringExpr::from_iter([module_path, StringExpr::from_str("::"), ident])
1293 }
1294 }
1295 }
1296 }
1297
1298 pub fn short_type_path(&self, bevy_reflect_path: &Path) -> StringExpr {
1302 match self {
1303 Self::Anonymous {
1304 short_type_path, ..
1305 } => short_type_path.clone(),
1306 Self::Primitive(ident) => StringExpr::from(ident),
1307 Self::External { generics, .. } | Self::Internal { generics, .. } => {
1308 let ident = self.type_ident().unwrap();
1309
1310 if self.impl_is_generic() {
1311 let generics = ReflectTypePath::reduce_generics(
1312 generics,
1313 |TypeParam { ident, .. }| {
1314 StringExpr::Borrowed(quote! {
1315 <#ident as #bevy_reflect_path::TypePath>::short_type_path()
1316 })
1317 },
1318 bevy_reflect_path,
1319 );
1320
1321 StringExpr::from_iter([
1322 ident,
1323 StringExpr::from_str("<"),
1324 generics,
1325 StringExpr::from_str(">"),
1326 ])
1327 } else {
1328 ident
1329 }
1330 }
1331 }
1332 }
1333
1334 pub fn module_path(&self) -> Option<StringExpr> {
1347 if let Some(path) = self.get_path() {
1348 let path_string = path
1349 .segments
1350 .pairs()
1351 .take(path.segments.len() - 1)
1352 .map(|pair| pair.value().ident.to_string())
1353 .reduce(|path, ident| path + "::" + &ident)
1354 .unwrap();
1355
1356 let path_lit = LitStr::new(&path_string, path.span());
1357 return Some(StringExpr::from_lit(&path_lit));
1358 }
1359
1360 match self {
1361 Self::Internal { .. } => Some(StringExpr::Const(quote! {
1362 ::core::module_path!()
1363 })),
1364 Self::External { .. } => unreachable!(),
1365 _ => None,
1366 }
1367 }
1368
1369 pub fn type_ident(&self) -> Option<StringExpr> {
1379 self.get_ident().map(StringExpr::from)
1380 }
1381
1382 pub fn true_type(&self) -> proc_macro2::TokenStream {
1388 match self {
1389 Self::Primitive(ident) => quote!(#ident),
1390 Self::Internal {
1391 ident, generics, ..
1392 } => {
1393 let (_, ty_generics, _) = generics.split_for_impl();
1394 quote!(#ident #ty_generics)
1395 }
1396 Self::External { path, generics, .. } => {
1397 let (_, ty_generics, _) = generics.split_for_impl();
1398 quote!(#path #ty_generics)
1399 }
1400 Self::Anonymous { qualified_type, .. } => qualified_type.to_token_stream(),
1401 }
1402 }
1403}
1404
1405impl<'a> ToTokens for ReflectTypePath<'a> {
1406 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
1407 match self {
1408 Self::Internal { ident, .. } | Self::Primitive(ident) => ident.to_tokens(tokens),
1409 Self::External { path, .. } => path.to_tokens(tokens),
1410 Self::Anonymous { qualified_type, .. } => qualified_type.to_tokens(tokens),
1411 }
1412 }
1413}