bevy_reflect_derive/impls/
typed.rs

1use crate::{
2    derive_data::{ReflectMeta, ReflectTypePath},
3    string_expr::StringExpr,
4    where_clause_options::WhereClauseOptions,
5};
6use bevy_macro_utils::fq_std::FQOption;
7use proc_macro2::TokenStream;
8use quote::{quote, ToTokens};
9
10/// Returns an expression for a `NonGenericTypeCell` or `GenericTypeCell`  to generate `'static` references.
11fn static_type_cell(
12    meta: &ReflectMeta,
13    property: TypedProperty,
14    generator: TokenStream,
15) -> TokenStream {
16    let bevy_reflect_path = meta.bevy_reflect_path();
17    if meta.type_path().impl_is_generic() {
18        let cell_type = match property {
19            TypedProperty::TypePath => quote!(GenericTypePathCell),
20            TypedProperty::TypeInfo => quote!(GenericTypeInfoCell),
21        };
22
23        quote! {
24            static CELL: #bevy_reflect_path::utility::#cell_type = #bevy_reflect_path::utility::#cell_type::new();
25            CELL.get_or_insert::<Self, _>(|| {
26                #generator
27            })
28        }
29    } else {
30        let cell_type = match property {
31            TypedProperty::TypePath => unreachable!(
32                "Cannot have a non-generic type path cell. Use string literals and core::concat instead."
33            ),
34            TypedProperty::TypeInfo => quote!(NonGenericTypeInfoCell),
35        };
36
37        quote! {
38            static CELL: #bevy_reflect_path::utility::#cell_type = #bevy_reflect_path::utility::#cell_type::new();
39            CELL.get_or_set(|| {
40                #generator
41            })
42        }
43    }
44}
45
46#[derive(Clone, Copy)]
47pub(crate) enum TypedProperty {
48    TypeInfo,
49    TypePath,
50}
51
52pub(crate) fn impl_type_path(meta: &ReflectMeta) -> TokenStream {
53    let where_clause_options = WhereClauseOptions::new(meta);
54
55    if !meta.attrs().type_path_attrs().should_auto_derive() {
56        return TokenStream::new();
57    }
58
59    let type_path = meta.type_path();
60    let bevy_reflect_path = meta.bevy_reflect_path();
61
62    let (long_type_path, short_type_path) = if type_path.impl_is_generic() {
63        let long_path_cell = static_type_cell(
64            meta,
65            TypedProperty::TypePath,
66            type_path.long_type_path(bevy_reflect_path).into_owned(),
67        );
68        let short_path_cell = static_type_cell(
69            meta,
70            TypedProperty::TypePath,
71            type_path.short_type_path(bevy_reflect_path).into_owned(),
72        );
73        (
74            long_path_cell.to_token_stream(),
75            short_path_cell.to_token_stream(),
76        )
77    } else {
78        (
79            type_path.long_type_path(bevy_reflect_path).into_borrowed(),
80            type_path.short_type_path(bevy_reflect_path).into_borrowed(),
81        )
82    };
83
84    let type_ident = wrap_in_option(type_path.type_ident().map(StringExpr::into_borrowed));
85    let module_path = wrap_in_option(type_path.module_path().map(StringExpr::into_borrowed));
86    let crate_name = wrap_in_option(type_path.crate_name().map(StringExpr::into_borrowed));
87
88    let primitive_assert = if let ReflectTypePath::Primitive(_) = type_path {
89        Some(quote! {
90            const _: () = {
91                mod private_scope {
92                    // Compiles if it can be named when there are no imports.
93                    type AssertIsPrimitive = #type_path;
94                }
95            };
96        })
97    } else {
98        None
99    };
100
101    let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
102
103    // Add Typed bound for each active field
104    let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
105
106    quote! {
107        #primitive_assert
108
109        impl #impl_generics #bevy_reflect_path::TypePath for #type_path #ty_generics #where_reflect_clause {
110            fn type_path() -> &'static str {
111                #long_type_path
112            }
113
114            fn short_type_path() -> &'static str {
115                #short_type_path
116            }
117
118            fn type_ident() -> Option<&'static str> {
119                #type_ident
120            }
121
122            fn crate_name() -> Option<&'static str> {
123                #crate_name
124            }
125
126            fn module_path() -> Option<&'static str> {
127                #module_path
128            }
129        }
130    }
131}
132
133pub(crate) fn impl_typed(
134    where_clause_options: &WhereClauseOptions,
135    type_info_generator: TokenStream,
136) -> TokenStream {
137    let meta = where_clause_options.meta();
138    let type_path = meta.type_path();
139    let bevy_reflect_path = meta.bevy_reflect_path();
140
141    let type_info_cell = static_type_cell(meta, TypedProperty::TypeInfo, type_info_generator);
142
143    let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
144
145    let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
146
147    quote! {
148        impl #impl_generics #bevy_reflect_path::Typed for #type_path #ty_generics #where_reflect_clause {
149            #[inline]
150            fn type_info() -> &'static #bevy_reflect_path::TypeInfo {
151                #type_info_cell
152            }
153        }
154    }
155}
156
157/// Turns an `Option<TokenStream>` into a `TokenStream` for an `Option`.
158fn wrap_in_option(tokens: Option<TokenStream>) -> TokenStream {
159    match tokens {
160        Some(tokens) => quote! {
161            #FQOption::Some(#tokens)
162        },
163        None => quote! {
164            #FQOption::None
165        },
166    }
167}