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        // To ensure alloc is available, but also prevent its name from clashing, we place the implementation inside an anonymous constant
110        const _: () = {
111            extern crate alloc;
112
113            use alloc::string::ToString;
114
115            impl #impl_generics #bevy_reflect_path::TypePath for #type_path #ty_generics #where_reflect_clause {
116                fn type_path() -> &'static str {
117                    #long_type_path
118                }
119
120                fn short_type_path() -> &'static str {
121                    #short_type_path
122                }
123
124                fn type_ident() -> Option<&'static str> {
125                    #type_ident
126                }
127
128                fn crate_name() -> Option<&'static str> {
129                    #crate_name
130                }
131
132                fn module_path() -> Option<&'static str> {
133                    #module_path
134                }
135            }
136        };
137    }
138}
139
140pub(crate) fn impl_typed(
141    meta: &ReflectMeta,
142    where_clause_options: &WhereClauseOptions,
143    type_info_generator: TokenStream,
144) -> TokenStream {
145    let type_path = meta.type_path();
146    let bevy_reflect_path = meta.bevy_reflect_path();
147
148    let type_info_cell = static_type_cell(meta, TypedProperty::TypeInfo, type_info_generator);
149
150    let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
151
152    let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
153
154    quote! {
155        impl #impl_generics #bevy_reflect_path::Typed for #type_path #ty_generics #where_reflect_clause {
156            #[inline]
157            fn type_info() -> &'static #bevy_reflect_path::TypeInfo {
158                #type_info_cell
159            }
160        }
161    }
162}
163
164/// Turns an `Option<TokenStream>` into a `TokenStream` for an `Option`.
165fn wrap_in_option(tokens: Option<TokenStream>) -> TokenStream {
166    match tokens {
167        Some(tokens) => quote! {
168            #FQOption::Some(#tokens)
169        },
170        None => quote! {
171            #FQOption::None
172        },
173    }
174}