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