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, MacroDelimiter};
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 = "reflect_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 = "reflect_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 = "reflect_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 = "reflect_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 if let MacroDelimiter::Paren(_) = meta_list.delimiter {
201 container_attributes.parse_meta_list(meta_list, provenance.trait_)?;
202 } else {
203 return Err(syn::Error::new(
204 meta_list.delimiter.span().join(),
205 format_args!(
206 "`#[{REFLECT_ATTRIBUTE_NAME}(\"...\")]` must use parentheses `(` and `)`"
207 ),
208 ));
209 }
210 }
211 Meta::NameValue(pair) if pair.path.is_ident(TYPE_PATH_ATTRIBUTE_NAME) => {
212 let syn::Expr::Lit(syn::ExprLit {
213 lit: syn::Lit::Str(lit),
214 ..
215 }) = &pair.value
216 else {
217 return Err(syn::Error::new(
218 pair.span(),
219 format_args!("`#[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"]` must be a string literal"),
220 ));
221 };
222
223 custom_path = Some(syn::parse::Parser::parse_str(
224 parse_path_no_leading_colon,
225 &lit.value(),
226 )?);
227 }
228 Meta::NameValue(pair) if pair.path.is_ident(TYPE_NAME_ATTRIBUTE_NAME) => {
229 let syn::Expr::Lit(syn::ExprLit {
230 lit: syn::Lit::Str(lit),
231 ..
232 }) = &pair.value
233 else {
234 return Err(syn::Error::new(
235 pair.span(),
236 format_args!("`#[{TYPE_NAME_ATTRIBUTE_NAME} = \"...\"]` must be a string literal"),
237 ));
238 };
239
240 custom_type_name = Some(parse_str(&lit.value())?);
241 }
242 #[cfg(feature = "reflect_documentation")]
243 Meta::NameValue(pair) if pair.path.is_ident("doc") => {
244 if let syn::Expr::Lit(syn::ExprLit {
245 lit: syn::Lit::Str(lit),
246 ..
247 }) = &pair.value
248 {
249 doc.push(lit.value());
250 }
251 }
252 _ => continue,
253 }
254 }
255 match (&mut custom_path, custom_type_name) {
256 (Some(path), custom_type_name) => {
257 let ident = custom_type_name.unwrap_or_else(|| input.ident.clone());
258 path.segments.push(PathSegment::from(ident));
259 }
260 (None, Some(name)) => {
261 return Err(syn::Error::new(
262 name.span(),
263 format!("cannot use `#[{TYPE_NAME_ATTRIBUTE_NAME} = \"...\"]` without a `#[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"]` attribute."),
264 ));
265 }
266 _ => (),
267 }
268
269 let type_path = ReflectTypePath::Internal {
270 ident: &input.ident,
271 custom_path,
272 generics: &input.generics,
273 };
274
275 let meta = ReflectMeta::new(type_path, container_attributes);
276
277 if provenance.source == ReflectImplSource::ImplRemoteType
278 && meta.type_path_attrs().should_auto_derive()
279 && !meta.type_path().has_custom_path()
280 {
281 return Err(syn::Error::new(
282 meta.type_path().span(),
283 format!("a #[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"] attribute must be specified when using {provenance}"),
284 ));
285 }
286
287 #[cfg(feature = "reflect_documentation")]
288 let meta = meta.with_docs(doc);
289
290 if meta.attrs().is_opaque() {
291 return Ok(Self::Opaque(meta));
292 }
293
294 match &input.data {
295 Data::Struct(data) => {
296 let fields = Self::collect_struct_fields(&data.fields)?;
297 let serialization_data =
298 SerializationDataDef::new(&fields, &meta.bevy_reflect_path)?;
299 let reflect_struct = ReflectStruct {
300 meta,
301 serialization_data,
302 fields,
303 };
304
305 match data.fields {
306 Fields::Named(..) => Ok(Self::Struct(reflect_struct)),
307 Fields::Unnamed(..) => Ok(Self::TupleStruct(reflect_struct)),
308 Fields::Unit => Ok(Self::UnitStruct(reflect_struct)),
309 }
310 }
311 Data::Enum(data) => {
312 let variants = Self::collect_enum_variants(&data.variants)?;
313
314 let reflect_enum = ReflectEnum { meta, variants };
315 Ok(Self::Enum(reflect_enum))
316 }
317 Data::Union(..) => Err(syn::Error::new(
318 input.span(),
319 "reflection not supported for unions",
320 )),
321 }
322 }
323
324 pub fn set_remote(&mut self, remote_ty: Option<RemoteType<'a>>) {
330 match self {
331 Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {
332 data.meta.remote_ty = remote_ty;
333 }
334 Self::Enum(data) => {
335 data.meta.remote_ty = remote_ty;
336 }
337 Self::Opaque(meta) => {
338 meta.remote_ty = remote_ty;
339 }
340 }
341 }
342
343 pub fn remote_ty(&self) -> Option<RemoteType<'_>> {
345 match self {
346 Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {
347 data.meta.remote_ty()
348 }
349 Self::Enum(data) => data.meta.remote_ty(),
350 Self::Opaque(meta) => meta.remote_ty(),
351 }
352 }
353
354 pub fn meta(&self) -> &ReflectMeta<'_> {
356 match self {
357 Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => data.meta(),
358 Self::Enum(data) => data.meta(),
359 Self::Opaque(meta) => meta,
360 }
361 }
362
363 pub fn where_clause_options(&self) -> WhereClauseOptions<'_, '_> {
364 match self {
365 Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {
366 data.where_clause_options()
367 }
368 Self::Enum(data) => data.where_clause_options(),
369 Self::Opaque(meta) => WhereClauseOptions::new(meta),
370 }
371 }
372
373 fn collect_struct_fields(fields: &'a Fields) -> Result<Vec<StructField<'a>>, syn::Error> {
374 let mut active_index = 0;
375 let sifter: ResultSifter<StructField<'a>> = fields
376 .iter()
377 .enumerate()
378 .map(
379 |(declaration_index, field)| -> Result<StructField, syn::Error> {
380 let attrs = FieldAttributes::parse_attributes(&field.attrs)?;
381
382 let reflection_index = if attrs.ignore.is_ignored() {
383 None
384 } else {
385 active_index += 1;
386 Some(active_index - 1)
387 };
388
389 Ok(StructField {
390 declaration_index,
391 reflection_index,
392 attrs,
393 data: field,
394 #[cfg(feature = "reflect_documentation")]
395 doc: crate::documentation::Documentation::from_attributes(&field.attrs),
396 })
397 },
398 )
399 .fold(ResultSifter::default(), ResultSifter::fold);
400
401 sifter.finish()
402 }
403
404 fn collect_enum_variants(
405 variants: &'a Punctuated<Variant, Comma>,
406 ) -> Result<Vec<EnumVariant<'a>>, syn::Error> {
407 let sifter: ResultSifter<EnumVariant<'a>> = variants
408 .iter()
409 .map(|variant| -> Result<EnumVariant, syn::Error> {
410 let fields = Self::collect_struct_fields(&variant.fields)?;
411
412 let fields = match variant.fields {
413 Fields::Named(..) => EnumVariantFields::Named(fields),
414 Fields::Unnamed(..) => EnumVariantFields::Unnamed(fields),
415 Fields::Unit => EnumVariantFields::Unit,
416 };
417 Ok(EnumVariant {
418 fields,
419 attrs: FieldAttributes::parse_attributes(&variant.attrs)?,
420 data: variant,
421 #[cfg(feature = "reflect_documentation")]
422 doc: crate::documentation::Documentation::from_attributes(&variant.attrs),
423 })
424 })
425 .fold(ResultSifter::default(), ResultSifter::fold);
426
427 sifter.finish()
428 }
429}
430
431impl<'a> ReflectMeta<'a> {
432 pub fn new(type_path: ReflectTypePath<'a>, attrs: ContainerAttributes) -> Self {
433 Self {
434 attrs,
435 type_path,
436 remote_ty: None,
437 bevy_reflect_path: crate::meta::get_bevy_reflect_path(),
438 #[cfg(feature = "reflect_documentation")]
439 docs: Default::default(),
440 }
441 }
442
443 #[cfg(feature = "reflect_documentation")]
445 pub fn with_docs(self, docs: crate::documentation::Documentation) -> Self {
446 Self { docs, ..self }
447 }
448
449 pub fn attrs(&self) -> &ContainerAttributes {
451 &self.attrs
452 }
453
454 #[expect(
456 clippy::wrong_self_convention,
457 reason = "Method returns `FromReflectAttrs`, does not actually convert data."
458 )]
459 pub fn from_reflect(&self) -> &FromReflectAttrs {
460 self.attrs.from_reflect_attrs()
461 }
462
463 pub fn type_path_attrs(&self) -> &TypePathAttrs {
465 self.attrs.type_path_attrs()
466 }
467
468 pub fn type_path(&self) -> &ReflectTypePath<'a> {
470 &self.type_path
471 }
472
473 pub fn remote_ty(&self) -> Option<RemoteType<'_>> {
475 self.remote_ty
476 }
477
478 pub fn is_remote_wrapper(&self) -> bool {
480 self.remote_ty.is_some()
481 }
482
483 pub fn bevy_reflect_path(&self) -> &Path {
485 &self.bevy_reflect_path
486 }
487
488 pub fn get_type_registration(
490 &self,
491 where_clause_options: &WhereClauseOptions,
492 ) -> proc_macro2::TokenStream {
493 crate::registration::impl_get_type_registration(
494 where_clause_options,
495 None,
496 Option::<core::iter::Empty<&Type>>::None,
497 )
498 }
499
500 #[cfg(feature = "reflect_documentation")]
502 pub fn doc(&self) -> &crate::documentation::Documentation {
503 &self.docs
504 }
505}
506
507impl<'a> StructField<'a> {
508 pub fn to_info_tokens(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {
510 let name = match &self.data.ident {
511 Some(ident) => ident.to_string().to_token_stream(),
512 None => self.reflection_index.to_token_stream(),
513 };
514
515 let field_info = if self.data.ident.is_some() {
516 quote! {
517 #bevy_reflect_path::NamedField
518 }
519 } else {
520 quote! {
521 #bevy_reflect_path::UnnamedField
522 }
523 };
524
525 let ty = self.reflected_type();
526
527 let mut info = quote! {
528 #field_info::new::<#ty>(#name)
529 };
530
531 let custom_attributes = &self.attrs.custom_attributes;
532 if !custom_attributes.is_empty() {
533 let custom_attributes = custom_attributes.to_tokens(bevy_reflect_path);
534 info.extend(quote! {
535 .with_custom_attributes(#custom_attributes)
536 });
537 }
538
539 #[cfg(feature = "reflect_documentation")]
540 {
541 let docs = &self.doc;
542 if !docs.is_empty() {
543 info.extend(quote! {
544 .with_docs(#docs)
545 });
546 }
547 }
548
549 info
550 }
551
552 pub fn reflected_type(&self) -> &Type {
558 self.attrs.remote.as_ref().unwrap_or(&self.data.ty)
559 }
560
561 pub fn attrs(&self) -> &FieldAttributes {
562 &self.attrs
563 }
564
565 pub fn to_member(&self) -> Member {
570 match &self.data.ident {
571 Some(ident) => Member::Named(ident.clone()),
572 None => Member::Unnamed(self.declaration_index.into()),
573 }
574 }
575
576 pub fn field_id(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {
578 match &self.data.ident {
579 Some(ident) => {
580 let name = ident.to_string();
581 quote!(#bevy_reflect_path::FieldId::Named(#bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(#name)))
582 }
583 None => {
584 let index = self.declaration_index;
585 quote!(#bevy_reflect_path::FieldId::Unnamed(#index))
586 }
587 }
588 }
589}
590
591impl<'a> ReflectStruct<'a> {
592 pub fn meta(&self) -> &ReflectMeta<'a> {
594 &self.meta
595 }
596
597 pub fn serialization_data(&self) -> Option<&SerializationDataDef> {
599 self.serialization_data.as_ref()
600 }
601
602 pub fn get_type_registration(
606 &self,
607 where_clause_options: &WhereClauseOptions,
608 ) -> proc_macro2::TokenStream {
609 crate::registration::impl_get_type_registration(
610 where_clause_options,
611 self.serialization_data(),
612 Some(self.active_types().iter()),
613 )
614 }
615
616 pub fn active_types(&self) -> IndexSet<Type> {
618 self.active_fields()
620 .map(|field| field.reflected_type().clone())
621 .collect::<IndexSet<_>>()
622 }
623
624 pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
626 self.fields()
627 .iter()
628 .filter(|field| field.attrs.ignore.is_active())
629 }
630
631 pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
633 self.fields()
634 .iter()
635 .filter(|field| field.attrs.ignore.is_ignored())
636 }
637
638 pub fn fields(&self) -> &[StructField<'a>] {
640 &self.fields
641 }
642
643 pub fn where_clause_options(&self) -> WhereClauseOptions<'_, '_> {
644 WhereClauseOptions::new_with_types(self.meta(), self.active_types())
645 }
646
647 pub fn to_info_tokens(&self, is_tuple: bool) -> proc_macro2::TokenStream {
649 let bevy_reflect_path = self.meta().bevy_reflect_path();
650
651 let (info_variant, info_struct) = if is_tuple {
652 (
653 Ident::new("TupleStruct", Span::call_site()),
654 Ident::new("TupleStructInfo", Span::call_site()),
655 )
656 } else {
657 (
658 Ident::new("Struct", Span::call_site()),
659 Ident::new("StructInfo", Span::call_site()),
660 )
661 };
662
663 let field_infos = self
664 .active_fields()
665 .map(|field| field.to_info_tokens(bevy_reflect_path));
666
667 let mut info = quote! {
668 #bevy_reflect_path::#info_struct::new::<Self>(&[
669 #(#field_infos),*
670 ])
671 };
672
673 let custom_attributes = self.meta.attrs.custom_attributes();
674 if !custom_attributes.is_empty() {
675 let custom_attributes = custom_attributes.to_tokens(bevy_reflect_path);
676 info.extend(quote! {
677 .with_custom_attributes(#custom_attributes)
678 });
679 }
680
681 if let Some(generics) = generate_generics(self.meta()) {
682 info.extend(quote! {
683 .with_generics(#generics)
684 });
685 }
686
687 #[cfg(feature = "reflect_documentation")]
688 {
689 let docs = self.meta().doc();
690 if !docs.is_empty() {
691 info.extend(quote! {
692 .with_docs(#docs)
693 });
694 }
695 }
696
697 quote! {
698 #bevy_reflect_path::TypeInfo::#info_variant(#info)
699 }
700 }
701 pub fn get_clone_impl(&self) -> Option<proc_macro2::TokenStream> {
703 let bevy_reflect_path = self.meta().bevy_reflect_path();
704
705 if let container_clone @ Some(_) = self.meta().attrs().get_clone_impl(bevy_reflect_path) {
706 return container_clone;
707 }
708
709 let mut tokens = proc_macro2::TokenStream::new();
710
711 for field in self.fields().iter() {
712 let field_ty = field.reflected_type();
713 let member = field.to_member();
714 let accessor = self.access_for_field(field, false);
715
716 match &field.attrs.clone {
717 CloneBehavior::Default => {
718 let value = if field.attrs.ignore.is_ignored() {
719 let field_id = field.field_id(bevy_reflect_path);
720
721 quote! {
722 return #FQResult::Err(#bevy_reflect_path::ReflectCloneError::FieldNotCloneable {
723 field: #field_id,
724 variant: #FQOption::None,
725 container_type_path: #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(
726 <Self as #bevy_reflect_path::TypePath>::type_path()
727 )
728 })
729 }
730 } else {
731 quote! {
732 <#field_ty as #bevy_reflect_path::PartialReflect>::reflect_clone_and_take(#accessor)?
733 }
734 };
735
736 tokens.extend(quote! {
737 #member: #value,
738 });
739 }
740 CloneBehavior::Trait => {
741 tokens.extend(quote! {
742 #member: #FQClone::clone(#accessor),
743 });
744 }
745 CloneBehavior::Func(clone_fn) => {
746 tokens.extend(quote! {
747 #member: #clone_fn(#accessor),
748 });
749 }
750 }
751 }
752
753 let ctor = match self.meta.remote_ty() {
754 Some(ty) => {
755 let ty = ty.as_expr_path().ok()?.to_token_stream();
756 quote! {
757 Self(#ty {
758 #tokens
759 })
760 }
761 }
762 None => {
763 quote! {
764 Self {
765 #tokens
766 }
767 }
768 }
769 };
770
771 Some(quote! {
772 #[inline]
773 #[allow(unreachable_code, reason = "Ignored fields without a `clone` attribute will early-return with an error")]
774 fn reflect_clone(&self) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>, #bevy_reflect_path::ReflectCloneError> {
775 #FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#ctor))
776 }
777 })
778 }
779
780 pub fn access_for_field(
789 &self,
790 field: &StructField<'a>,
791 is_mutable: bool,
792 ) -> proc_macro2::TokenStream {
793 let bevy_reflect_path = self.meta().bevy_reflect_path();
794 let member = field.to_member();
795
796 let prefix_tokens = if is_mutable { quote!(&mut) } else { quote!(&) };
797
798 let accessor = if self.meta.is_remote_wrapper() {
799 quote!(self.0.#member)
800 } else {
801 quote!(self.#member)
802 };
803
804 match &field.attrs.remote {
805 Some(wrapper_ty) => {
806 let method = if is_mutable {
807 format_ident!("as_wrapper_mut")
808 } else {
809 format_ident!("as_wrapper")
810 };
811
812 quote! {
813 <#wrapper_ty as #bevy_reflect_path::ReflectRemote>::#method(#prefix_tokens #accessor)
814 }
815 }
816 None => quote!(#prefix_tokens #accessor),
817 }
818 }
819}
820
821impl<'a> ReflectEnum<'a> {
822 pub fn meta(&self) -> &ReflectMeta<'a> {
824 &self.meta
825 }
826
827 pub fn get_unit(&self, variant: &Ident) -> proc_macro2::TokenStream {
831 let name = self
832 .meta
833 .remote_ty
834 .map(|path| match path.as_expr_path() {
835 Ok(path) => path.to_token_stream(),
836 Err(err) => err.into_compile_error(),
837 })
838 .unwrap_or_else(|| self.meta.type_path().to_token_stream());
839
840 quote! {
841 #name::#variant
842 }
843 }
844
845 pub fn variants(&self) -> &[EnumVariant<'a>] {
847 &self.variants
848 }
849
850 pub fn active_types(&self) -> IndexSet<Type> {
852 self.active_fields()
854 .map(|field| field.reflected_type().clone())
855 .collect::<IndexSet<_>>()
856 }
857
858 pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
860 self.variants.iter().flat_map(EnumVariant::active_fields)
861 }
862
863 pub fn where_clause_options(&self) -> WhereClauseOptions<'_, '_> {
864 WhereClauseOptions::new_with_types(self.meta(), self.active_types())
865 }
866
867 pub fn get_type_registration(
871 &self,
872 where_clause_options: &WhereClauseOptions,
873 ) -> proc_macro2::TokenStream {
874 crate::registration::impl_get_type_registration(
875 where_clause_options,
876 None,
877 Some(self.active_types().iter()),
878 )
879 }
880
881 pub fn to_info_tokens(&self) -> proc_macro2::TokenStream {
883 let bevy_reflect_path = self.meta().bevy_reflect_path();
884
885 let variants = self
886 .variants
887 .iter()
888 .map(|variant| variant.to_info_tokens(bevy_reflect_path));
889
890 let mut info = quote! {
891 #bevy_reflect_path::EnumInfo::new::<Self>(&[
892 #(#variants),*
893 ])
894 };
895
896 let custom_attributes = self.meta.attrs.custom_attributes();
897 if !custom_attributes.is_empty() {
898 let custom_attributes = custom_attributes.to_tokens(bevy_reflect_path);
899 info.extend(quote! {
900 .with_custom_attributes(#custom_attributes)
901 });
902 }
903
904 if let Some(generics) = generate_generics(self.meta()) {
905 info.extend(quote! {
906 .with_generics(#generics)
907 });
908 }
909
910 #[cfg(feature = "reflect_documentation")]
911 {
912 let docs = self.meta().doc();
913 if !docs.is_empty() {
914 info.extend(quote! {
915 .with_docs(#docs)
916 });
917 }
918 }
919
920 quote! {
921 #bevy_reflect_path::TypeInfo::Enum(#info)
922 }
923 }
924
925 pub fn get_clone_impl(&self) -> Option<proc_macro2::TokenStream> {
927 let bevy_reflect_path = self.meta().bevy_reflect_path();
928
929 if let container_clone @ Some(_) = self.meta().attrs().get_clone_impl(bevy_reflect_path) {
930 return container_clone;
931 }
932
933 let this = Ident::new("this", Span::call_site());
934 let EnumVariantOutputData {
935 variant_patterns,
936 variant_constructors,
937 ..
938 } = ReflectCloneVariantBuilder::new(self).build(&this);
939
940 let inner = quote! {
941 match #this {
942 #(#variant_patterns => #variant_constructors),*
943 }
944 };
945
946 let body = if variant_patterns.is_empty() {
947 quote!(unreachable!())
949 } else if self.meta.is_remote_wrapper() {
950 quote! {
951 let #this = <Self as #bevy_reflect_path::ReflectRemote>::as_remote(self);
952 #FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(<Self as #bevy_reflect_path::ReflectRemote>::into_wrapper(#inner)))
953 }
954 } else {
955 quote! {
956 let #this = self;
957 #FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#inner))
958 }
959 };
960
961 Some(quote! {
962 #[inline]
963 #[allow(unreachable_code, reason = "Ignored fields without a `clone` attribute will early-return with an error")]
964 fn reflect_clone(&self) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>, #bevy_reflect_path::ReflectCloneError> {
965 #body
966 }
967 })
968 }
969}
970
971impl<'a> EnumVariant<'a> {
972 pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
974 self.fields()
975 .iter()
976 .filter(|field| field.attrs.ignore.is_active())
977 }
978
979 pub fn fields(&self) -> &[StructField<'a>] {
981 match &self.fields {
982 EnumVariantFields::Named(fields) | EnumVariantFields::Unnamed(fields) => fields,
983 EnumVariantFields::Unit => &[],
984 }
985 }
986
987 pub fn to_info_tokens(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {
989 let variant_name = &self.data.ident.to_string();
990
991 let (info_variant, info_struct) = match &self.fields {
992 EnumVariantFields::Unit => (
993 Ident::new("Unit", Span::call_site()),
994 Ident::new("UnitVariantInfo", Span::call_site()),
995 ),
996 EnumVariantFields::Unnamed(..) => (
997 Ident::new("Tuple", Span::call_site()),
998 Ident::new("TupleVariantInfo", Span::call_site()),
999 ),
1000 EnumVariantFields::Named(..) => (
1001 Ident::new("Struct", Span::call_site()),
1002 Ident::new("StructVariantInfo", Span::call_site()),
1003 ),
1004 };
1005
1006 let fields = self
1007 .active_fields()
1008 .map(|field| field.to_info_tokens(bevy_reflect_path));
1009
1010 let args = match &self.fields {
1011 EnumVariantFields::Unit => quote!(#variant_name),
1012 _ => {
1013 quote!( #variant_name , &[#(#fields),*] )
1014 }
1015 };
1016
1017 let mut info = quote! {
1018 #bevy_reflect_path::#info_struct::new(#args)
1019 };
1020
1021 let custom_attributes = &self.attrs.custom_attributes;
1022 if !custom_attributes.is_empty() {
1023 let custom_attributes = custom_attributes.to_tokens(bevy_reflect_path);
1024 info.extend(quote! {
1025 .with_custom_attributes(#custom_attributes)
1026 });
1027 }
1028
1029 #[cfg(feature = "reflect_documentation")]
1030 {
1031 let docs = &self.doc;
1032 if !docs.is_empty() {
1033 info.extend(quote! {
1034 .with_docs(#docs)
1035 });
1036 }
1037 }
1038
1039 quote! {
1040 #bevy_reflect_path::VariantInfo::#info_variant(#info)
1041 }
1042 }
1043}
1044
1045pub(crate) enum ReflectTypePath<'a> {
1080 Primitive(&'a Ident),
1082 External {
1086 path: &'a Path,
1087 custom_path: Option<Path>,
1088 generics: &'a Generics,
1089 },
1090 Internal {
1099 ident: &'a Ident,
1100 custom_path: Option<Path>,
1101 generics: &'a Generics,
1102 },
1103 #[expect(
1105 dead_code,
1106 reason = "Not currently used but may be useful in the future due to its generality."
1107 )]
1108 Anonymous {
1109 qualified_type: Box<Type>,
1110 long_type_path: StringExpr,
1111 short_type_path: StringExpr,
1112 },
1113}
1114
1115impl<'a> ReflectTypePath<'a> {
1116 pub fn get_ident(&self) -> Option<&Ident> {
1122 match self {
1123 Self::Internal {
1124 ident, custom_path, ..
1125 } => Some(
1126 custom_path
1127 .as_ref()
1128 .map(|path| &path.segments.last().unwrap().ident)
1129 .unwrap_or(ident),
1130 ),
1131 Self::External {
1132 path, custom_path, ..
1133 } => Some(
1134 &custom_path
1135 .as_ref()
1136 .unwrap_or(path)
1137 .segments
1138 .last()
1139 .unwrap()
1140 .ident,
1141 ),
1142 Self::Primitive(ident) => Some(ident),
1143 _ => None,
1144 }
1145 }
1146
1147 pub fn generics(&self) -> &'a Generics {
1154 const EMPTY_GENERICS: &Generics = &Generics {
1156 gt_token: None,
1157 lt_token: None,
1158 where_clause: None,
1159 params: Punctuated::new(),
1160 };
1161
1162 match self {
1163 Self::Internal { generics, .. } | Self::External { generics, .. } => generics,
1164 _ => EMPTY_GENERICS,
1165 }
1166 }
1167
1168 pub fn impl_is_generic(&self) -> bool {
1172 !self
1175 .generics()
1176 .params
1177 .iter()
1178 .all(|param| matches!(param, GenericParam::Lifetime(_)))
1179 }
1180
1181 pub fn get_path(&self) -> Option<&Path> {
1189 match self {
1190 Self::Internal { custom_path, .. } => custom_path.as_ref(),
1191 Self::External {
1192 path, custom_path, ..
1193 } => Some(custom_path.as_ref().unwrap_or(path)),
1194 _ => None,
1195 }
1196 }
1197
1198 pub fn has_custom_path(&self) -> bool {
1203 match self {
1204 Self::Internal { custom_path, .. } | Self::External { custom_path, .. } => {
1205 custom_path.is_some()
1206 }
1207 _ => false,
1208 }
1209 }
1210
1211 pub fn crate_name(&self) -> Option<StringExpr> {
1223 if let Some(path) = self.get_path() {
1224 let crate_name = &path.segments.first().unwrap().ident;
1225 return Some(StringExpr::from(crate_name));
1226 }
1227
1228 match self {
1229 Self::Internal { .. } => Some(StringExpr::Borrowed(quote! {
1230 ::core::module_path!()
1231 .split(':')
1232 .next()
1233 .unwrap()
1234 })),
1235 Self::External { .. } => unreachable!(),
1236 _ => None,
1237 }
1238 }
1239
1240 fn reduce_generics(
1246 generics: &Generics,
1247 mut ty_generic_fn: impl FnMut(&TypeParam) -> StringExpr,
1248 bevy_reflect_path: &Path,
1249 ) -> StringExpr {
1250 let mut params = generics.params.iter().filter_map(|param| match param {
1251 GenericParam::Type(type_param) => Some(ty_generic_fn(type_param)),
1252 GenericParam::Const(const_param) => {
1253 let ident = &const_param.ident;
1254 let ty = &const_param.ty;
1255
1256 Some(StringExpr::Owned(quote! {
1257 <#ty as #bevy_reflect_path::__macro_exports::alloc_utils::ToString>::to_string(&#ident)
1258 }))
1259 }
1260 GenericParam::Lifetime(_) => None,
1261 });
1262
1263 params
1264 .next()
1265 .into_iter()
1266 .chain(params.flat_map(|x| [StringExpr::from_str(", "), x]))
1267 .collect()
1268 }
1269
1270 pub fn long_type_path(&self, bevy_reflect_path: &Path) -> StringExpr {
1274 match self {
1275 Self::Primitive(ident) => StringExpr::from(ident),
1276 Self::Anonymous { long_type_path, .. } => long_type_path.clone(),
1277 Self::Internal { generics, .. } | Self::External { generics, .. } => {
1278 let ident = self.type_ident().unwrap();
1279 let module_path = self.module_path().unwrap();
1280
1281 if self.impl_is_generic() {
1282 let generics = ReflectTypePath::reduce_generics(
1283 generics,
1284 |TypeParam { ident, .. }| {
1285 StringExpr::Borrowed(quote! {
1286 <#ident as #bevy_reflect_path::TypePath>::type_path()
1287 })
1288 },
1289 bevy_reflect_path,
1290 );
1291
1292 StringExpr::from_iter([
1293 module_path,
1294 StringExpr::from_str("::"),
1295 ident,
1296 StringExpr::from_str("<"),
1297 generics,
1298 StringExpr::from_str(">"),
1299 ])
1300 } else {
1301 StringExpr::from_iter([module_path, StringExpr::from_str("::"), ident])
1302 }
1303 }
1304 }
1305 }
1306
1307 pub fn short_type_path(&self, bevy_reflect_path: &Path) -> StringExpr {
1311 match self {
1312 Self::Anonymous {
1313 short_type_path, ..
1314 } => short_type_path.clone(),
1315 Self::Primitive(ident) => StringExpr::from(ident),
1316 Self::External { generics, .. } | Self::Internal { generics, .. } => {
1317 let ident = self.type_ident().unwrap();
1318
1319 if self.impl_is_generic() {
1320 let generics = ReflectTypePath::reduce_generics(
1321 generics,
1322 |TypeParam { ident, .. }| {
1323 StringExpr::Borrowed(quote! {
1324 <#ident as #bevy_reflect_path::TypePath>::short_type_path()
1325 })
1326 },
1327 bevy_reflect_path,
1328 );
1329
1330 StringExpr::from_iter([
1331 ident,
1332 StringExpr::from_str("<"),
1333 generics,
1334 StringExpr::from_str(">"),
1335 ])
1336 } else {
1337 ident
1338 }
1339 }
1340 }
1341 }
1342
1343 pub fn module_path(&self) -> Option<StringExpr> {
1356 if let Some(path) = self.get_path() {
1357 let path_string = path
1358 .segments
1359 .pairs()
1360 .take(path.segments.len() - 1)
1361 .map(|pair| pair.value().ident.to_string())
1362 .reduce(|path, ident| path + "::" + &ident)
1363 .unwrap();
1364
1365 let path_lit = LitStr::new(&path_string, path.span());
1366 return Some(StringExpr::from_lit(&path_lit));
1367 }
1368
1369 match self {
1370 Self::Internal { .. } => Some(StringExpr::Const(quote! {
1371 ::core::module_path!()
1372 })),
1373 Self::External { .. } => unreachable!(),
1374 _ => None,
1375 }
1376 }
1377
1378 pub fn type_ident(&self) -> Option<StringExpr> {
1388 self.get_ident().map(StringExpr::from)
1389 }
1390
1391 pub fn true_type(&self) -> proc_macro2::TokenStream {
1397 match self {
1398 Self::Primitive(ident) => quote!(#ident),
1399 Self::Internal {
1400 ident, generics, ..
1401 } => {
1402 let (_, ty_generics, _) = generics.split_for_impl();
1403 quote!(#ident #ty_generics)
1404 }
1405 Self::External { path, generics, .. } => {
1406 let (_, ty_generics, _) = generics.split_for_impl();
1407 quote!(#path #ty_generics)
1408 }
1409 Self::Anonymous { qualified_type, .. } => qualified_type.to_token_stream(),
1410 }
1411 }
1412}
1413
1414impl<'a> ToTokens for ReflectTypePath<'a> {
1415 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
1416 match self {
1417 Self::Internal { ident, .. } | Self::Primitive(ident) => ident.to_tokens(tokens),
1418 Self::External { path, .. } => path.to_tokens(tokens),
1419 Self::Anonymous { qualified_type, .. } => qualified_type.to_tokens(tokens),
1420 }
1421 }
1422}