@@ -9,7 +9,7 @@ pub use shape::*;
9
9
pub use symbol:: * ;
10
10
11
11
use proc_macro:: TokenStream ;
12
- use quote:: { quote, quote_spanned} ;
12
+ use quote:: { format_ident , quote, quote_spanned} ;
13
13
use std:: { env, path:: PathBuf } ;
14
14
use syn:: spanned:: Spanned ;
15
15
use toml:: { map:: Map , Value } ;
@@ -105,6 +105,22 @@ impl BevyManifest {
105
105
}
106
106
}
107
107
108
+ fn is_attr < T : syn:: parse:: Parse + syn:: token:: CustomToken > (
109
+ attr : & syn:: Attribute ,
110
+ attr_name : & str ,
111
+ ) -> bool {
112
+ if attr. path . get_ident ( ) . as_ref ( ) . unwrap ( ) != & attr_name {
113
+ return false ;
114
+ }
115
+
116
+ attr. parse_args_with ( |input : syn:: parse:: ParseStream | {
117
+ let ignore = input. parse :: < Option < T > > ( ) ?. is_some ( ) ;
118
+ Ok ( ignore)
119
+ } )
120
+ . ok ( )
121
+ == Some ( true )
122
+ }
123
+
108
124
/// Derive a label trait
109
125
///
110
126
/// # Args
@@ -114,32 +130,49 @@ impl BevyManifest {
114
130
pub fn derive_label (
115
131
input : syn:: DeriveInput ,
116
132
trait_path : & syn:: Path ,
133
+ id_path : & syn:: Path ,
117
134
attr_name : & str ,
118
135
) -> TokenStream {
119
- // return true if the variant specified is an `ignore_fields` attribute
120
- fn is_ignore ( attr : & syn:: Attribute , attr_name : & str ) -> bool {
121
- if attr. path . get_ident ( ) . as_ref ( ) . unwrap ( ) != & attr_name {
122
- return false ;
123
- }
124
-
125
- syn:: custom_keyword!( ignore_fields) ;
126
- attr. parse_args_with ( |input : syn:: parse:: ParseStream | {
127
- let ignore = input. parse :: < Option < ignore_fields > > ( ) ?. is_some ( ) ;
128
- Ok ( ignore)
129
- } )
130
- . unwrap ( )
136
+ fn is_hash_attr ( attr : & syn:: Attribute , attr_name : & str ) -> bool {
137
+ syn:: custom_keyword!( hash) ;
138
+ is_attr :: < hash > ( attr, attr_name)
131
139
}
132
140
133
- let ident = input. ident . clone ( ) ;
141
+ // If the label is marked with `#[label(hash)]`.
142
+ let hash_attr = input. attrs . iter ( ) . any ( |a| is_hash_attr ( a, attr_name) ) ;
134
143
135
- let ( impl_generics, ty_generics, where_clause) = input. generics . split_for_impl ( ) ;
144
+ if hash_attr {
145
+ derive_hashed_label ( input, trait_path, id_path, attr_name)
146
+ } else {
147
+ derive_named_label ( input, trait_path, attr_name)
148
+ }
149
+ }
150
+
151
+ fn with_static_bound ( where_clause : Option < & syn:: WhereClause > ) -> syn:: WhereClause {
136
152
let mut where_clause = where_clause. cloned ( ) . unwrap_or_else ( || syn:: WhereClause {
137
153
where_token : Default :: default ( ) ,
138
154
predicates : Default :: default ( ) ,
139
155
} ) ;
140
156
where_clause
141
157
. predicates
142
158
. push ( syn:: parse2 ( quote ! { Self : ' static } ) . unwrap ( ) ) ;
159
+ where_clause
160
+ }
161
+
162
+ fn derive_named_label (
163
+ input : syn:: DeriveInput ,
164
+ trait_path : & syn:: Path ,
165
+ attr_name : & str ,
166
+ ) -> TokenStream {
167
+ // return true if the variant specified is an `ignore_fields` attribute
168
+ fn is_ignore ( attr : & syn:: Attribute , attr_name : & str ) -> bool {
169
+ syn:: custom_keyword!( ignore_fields) ;
170
+ is_attr :: < ignore_fields > ( attr, attr_name)
171
+ }
172
+
173
+ let ident = input. ident . clone ( ) ;
174
+ let ( impl_generics, ty_generics, where_clause) = input. generics . split_for_impl ( ) ;
175
+ let where_clause = with_static_bound ( where_clause) ;
143
176
144
177
let ( data, mut fmt) = match input. data {
145
178
syn:: Data :: Struct ( d) => {
@@ -260,3 +293,68 @@ pub fn derive_label(
260
293
} )
261
294
. into ( )
262
295
}
296
+
297
+ fn derive_hashed_label (
298
+ input : syn:: DeriveInput ,
299
+ trait_path : & syn:: Path ,
300
+ id_path : & syn:: Path ,
301
+ _attr_name : & str ,
302
+ ) -> TokenStream {
303
+ let manifest = BevyManifest :: default ( ) ;
304
+
305
+ let ident = input. ident ;
306
+ let ( impl_generics, ty_generics, where_clause) = input. generics . split_for_impl ( ) ;
307
+ let where_clause = with_static_bound ( where_clause) ;
308
+
309
+ let compute_hash_path = {
310
+ let mut path = manifest. get_path ( "bevy_utils" ) ;
311
+ path. segments
312
+ . push ( format_ident ! ( "compute_fixed_hash" ) . into ( ) ) ;
313
+ path
314
+ } ;
315
+ let interner_type_path = {
316
+ let mut path = manifest. get_path ( "bevy_ecs" ) ;
317
+ path. segments . push ( format_ident ! ( "schedule" ) . into ( ) ) ;
318
+ path. segments . push ( format_ident ! ( "Labels" ) . into ( ) ) ;
319
+ path
320
+ } ;
321
+ let guard_type_path = {
322
+ let mut path = manifest. get_path ( "bevy_ecs" ) ;
323
+ path. segments . push ( format_ident ! ( "schedule" ) . into ( ) ) ;
324
+ path. segments . push ( format_ident ! ( "LabelGuard" ) . into ( ) ) ;
325
+ path
326
+ } ;
327
+ let interner_ident = format_ident ! ( "{}_INTERN" , ident. to_string( ) . to_uppercase( ) ) ;
328
+ let downcast_trait_path = {
329
+ let mut path = manifest. get_path ( "bevy_utils" ) ;
330
+ path. segments . push ( format_ident ! ( "label" ) . into ( ) ) ;
331
+ path. segments . push ( format_ident ! ( "LabelDowncast" ) . into ( ) ) ;
332
+ path
333
+ } ;
334
+
335
+ quote ! {
336
+ static #interner_ident : #interner_type_path <#ident #ty_generics> = #interner_type_path:: new( ) ;
337
+
338
+ impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
339
+ #[ inline]
340
+ fn data( & self ) -> u64 {
341
+ let hash = #compute_hash_path( self ) ;
342
+ #interner_ident . intern( hash, || :: std:: clone:: Clone :: clone( self ) ) ;
343
+ hash
344
+ }
345
+ fn fmt( hash: u64 , f: & mut :: std:: fmt:: Formatter ) -> :: std:: fmt:: Result {
346
+ #interner_ident
347
+ . scope( hash, |val| :: std:: fmt:: Debug :: fmt( val, f) )
348
+ . ok_or( :: std:: fmt:: Error ) ?
349
+ }
350
+ }
351
+
352
+ impl #impl_generics #downcast_trait_path <#id_path> for #ident #ty_generics #where_clause {
353
+ type Output = #guard_type_path <' static , Self >;
354
+ fn downcast_from( label: #id_path) -> Option <Self :: Output > {
355
+ let hash = <#id_path as #trait_path>:: data( & label) ;
356
+ #interner_ident . get( hash)
357
+ }
358
+ }
359
+ } . into ( )
360
+ }
0 commit comments