@@ -9,7 +9,8 @@ pub use shape::*;
9
9
pub use symbol:: * ;
10
10
11
11
use proc_macro:: TokenStream ;
12
- use quote:: { quote, quote_spanned} ;
12
+ use proc_macro2:: { Span , TokenStream as TokenStream2 } ;
13
+ use quote:: quote;
13
14
use std:: { env, path:: PathBuf } ;
14
15
use syn:: spanned:: Spanned ;
15
16
use toml:: { map:: Map , Value } ;
@@ -105,6 +106,55 @@ impl BevyManifest {
105
106
}
106
107
}
107
108
109
+ /// A set of attributes defined on an item, variant, or field,
110
+ /// in the form e.g. `#[system_label(..)]`.
111
+ #[ derive( Default ) ]
112
+ struct LabelAttrs {
113
+ ignore_fields : Option < Span > ,
114
+ }
115
+
116
+ impl LabelAttrs {
117
+ /// Parses a list of attributes.
118
+ ///
119
+ /// Ignores any that aren't of the form `#[my_label(..)]`.
120
+ /// Returns `Ok` if the iterator is empty.
121
+ pub fn new < ' a > (
122
+ iter : impl IntoIterator < Item = & ' a syn:: Attribute > ,
123
+ attr_name : & str ,
124
+ ) -> syn:: Result < Self > {
125
+ let mut this = Self :: default ( ) ;
126
+ for attr in iter {
127
+ // If it's not of the form `#[my_label(..)]`, skip it.
128
+ if attr. path . get_ident ( ) . as_ref ( ) . unwrap ( ) != & attr_name {
129
+ continue ;
130
+ }
131
+
132
+ // Parse the argument/s to the attribute.
133
+ attr. parse_args_with ( |input : syn:: parse:: ParseStream | {
134
+ loop {
135
+ syn:: custom_keyword!( ignore_fields) ;
136
+
137
+ let next = input. lookahead1 ( ) ;
138
+ if next. peek ( ignore_fields) {
139
+ let kw: ignore_fields = input. parse ( ) ?;
140
+ this. ignore_fields = Some ( kw. span ) ;
141
+ } else {
142
+ return Err ( next. error ( ) ) ;
143
+ }
144
+
145
+ if input. is_empty ( ) {
146
+ break ;
147
+ }
148
+ let _comma: syn:: Token ![ , ] = input. parse ( ) ?;
149
+ }
150
+ Ok ( ( ) )
151
+ } ) ?;
152
+ }
153
+
154
+ Ok ( this)
155
+ }
156
+ }
157
+
108
158
/// Derive a label trait
109
159
///
110
160
/// # Args
@@ -116,103 +166,120 @@ pub fn derive_label(
116
166
trait_path : & syn:: Path ,
117
167
attr_name : & str ,
118
168
) -> 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 ( )
131
- }
169
+ let item_attrs = match LabelAttrs :: new ( & input. attrs , attr_name) {
170
+ Ok ( a) => a,
171
+ Err ( e) => return e. into_compile_error ( ) . into ( ) ,
172
+ } ;
132
173
133
- let ident = input. ident . clone ( ) ;
174
+ derive_named_label ( input, & item_attrs, trait_path, attr_name)
175
+ . unwrap_or_else ( syn:: Error :: into_compile_error)
176
+ . into ( )
177
+ }
134
178
135
- let ( impl_generics , ty_generics , where_clause) = input . generics . split_for_impl ( ) ;
179
+ fn with_static_bound ( where_clause : Option < & syn :: WhereClause > ) -> syn :: WhereClause {
136
180
let mut where_clause = where_clause. cloned ( ) . unwrap_or_else ( || syn:: WhereClause {
137
181
where_token : Default :: default ( ) ,
138
182
predicates : Default :: default ( ) ,
139
183
} ) ;
140
184
where_clause
141
185
. predicates
142
186
. push ( syn:: parse2 ( quote ! { Self : ' static } ) . unwrap ( ) ) ;
187
+ where_clause
188
+ }
189
+
190
+ fn derive_named_label (
191
+ input : syn:: DeriveInput ,
192
+ item_attrs : & LabelAttrs ,
193
+ trait_path : & syn:: Path ,
194
+ attr_name : & str ,
195
+ ) -> syn:: Result < TokenStream2 > {
196
+ let ident = input. ident . clone ( ) ;
197
+ let ( impl_generics, ty_generics, where_clause) = input. generics . split_for_impl ( ) ;
198
+ let where_clause = with_static_bound ( where_clause) ;
143
199
144
- let as_str = match input. data {
200
+ let ( data , fmt ) = match input. data {
145
201
syn:: Data :: Struct ( d) => {
202
+ let all_field_attrs =
203
+ LabelAttrs :: new ( d. fields . iter ( ) . flat_map ( |f| & f. attrs ) , attr_name) ?;
146
204
// see if the user tried to ignore fields incorrectly
147
- if let Some ( attr) = d
148
- . fields
149
- . iter ( )
150
- . flat_map ( |f| & f. attrs )
151
- . find ( |a| is_ignore ( a, attr_name) )
152
- {
153
- let err_msg = format ! ( "`#[{attr_name}(ignore_fields)]` cannot be applied to fields individually: add it to the struct declaration" ) ;
154
- return quote_spanned ! {
155
- attr. span( ) => compile_error!( #err_msg) ;
156
- }
157
- . into ( ) ;
205
+ if let Some ( attr) = all_field_attrs. ignore_fields {
206
+ let err_msg = format ! (
207
+ r#"`#[{attr_name}(ignore_fields)]` cannot be applied to fields individually:
208
+ try adding it to the struct declaration"#
209
+ ) ;
210
+ return Err ( syn:: Error :: new ( attr, err_msg) ) ;
158
211
}
159
212
// Structs must either be fieldless, or explicitly ignore the fields.
160
- let ignore_fields = input . attrs . iter ( ) . any ( |a| is_ignore ( a , attr_name ) ) ;
161
- if matches ! ( d. fields, syn :: Fields :: Unit ) || ignore_fields {
213
+ let ignore_fields = item_attrs . ignore_fields . is_some ( ) ;
214
+ if d. fields . is_empty ( ) || ignore_fields {
162
215
let lit = ident. to_string ( ) ;
163
- quote ! { #lit }
216
+ let data = quote ! { 0 } ;
217
+ let as_str = quote ! { write!( f, #lit) } ;
218
+ ( data, as_str)
164
219
} else {
165
220
let err_msg = format ! ( "Labels cannot contain data, unless explicitly ignored with `#[{attr_name}(ignore_fields)]`" ) ;
166
- return quote_spanned ! {
167
- d. fields. span( ) => compile_error!( #err_msg) ;
168
- }
169
- . into ( ) ;
221
+ return Err ( syn:: Error :: new ( d. fields . span ( ) , err_msg) ) ;
170
222
}
171
223
}
172
224
syn:: Data :: Enum ( d) => {
173
225
// check if the user put #[label(ignore_fields)] in the wrong place
174
- if let Some ( attr) = input . attrs . iter ( ) . find ( |a| is_ignore ( a , attr_name ) ) {
226
+ if let Some ( attr) = item_attrs . ignore_fields {
175
227
let err_msg = format ! ( "`#[{attr_name}(ignore_fields)]` can only be applied to enum variants or struct declarations" ) ;
176
- return quote_spanned ! {
177
- attr. span( ) => compile_error!( #err_msg) ;
178
- }
179
- . into ( ) ;
228
+ return Err ( syn:: Error :: new ( attr, err_msg) ) ;
180
229
}
181
- let arms = d. variants . iter ( ) . map ( |v| {
230
+
231
+ let mut data_arms = Vec :: with_capacity ( d. variants . len ( ) ) ;
232
+ let mut fmt_arms = Vec :: with_capacity ( d. variants . len ( ) ) ;
233
+
234
+ for ( i, v) in d. variants . iter ( ) . enumerate ( ) {
235
+ let v_attrs = LabelAttrs :: new ( & v. attrs , attr_name) ?;
182
236
// Variants must either be fieldless, or explicitly ignore the fields.
183
- let ignore_fields = v . attrs . iter ( ) . any ( |a| is_ignore ( a , attr_name ) ) ;
184
- if matches ! ( v. fields, syn :: Fields :: Unit ) | ignore_fields {
237
+ let ignore_fields = v_attrs . ignore_fields . is_some ( ) ;
238
+ if v. fields . is_empty ( ) | | ignore_fields {
185
239
let mut path = syn:: Path :: from ( ident. clone ( ) ) ;
186
240
path. segments . push ( v. ident . clone ( ) . into ( ) ) ;
241
+
242
+ let i = i as u64 ;
243
+ data_arms. push ( quote ! { #path { .. } => #i } ) ;
244
+
187
245
let lit = format ! ( "{ident}::{}" , v. ident. clone( ) ) ;
188
- quote ! { #path { .. } => #lit }
246
+ fmt_arms . push ( quote ! { #i => { write! ( f , #lit) } } ) ;
189
247
} else {
190
248
let err_msg = format ! ( "Label variants cannot contain data, unless explicitly ignored with `#[{attr_name}(ignore_fields)]`" ) ;
191
- quote_spanned ! {
192
- v. fields. span( ) => _ => { compile_error!( #err_msg) ; }
193
- }
249
+ return Err ( syn:: Error :: new ( v. fields . span ( ) , err_msg) ) ;
194
250
}
195
- } ) ;
196
- quote ! {
251
+ }
252
+
253
+ let data = quote ! {
197
254
match self {
198
- #( #arms ) , *
255
+ #( #data_arms ) , *
199
256
}
200
- }
257
+ } ;
258
+ let fmt = quote ! {
259
+ match data {
260
+ #( #fmt_arms) , *
261
+ _ => Err ( :: std:: fmt:: Error ) ,
262
+ }
263
+ } ;
264
+ ( data, fmt)
201
265
}
202
266
syn:: Data :: Union ( _) => {
203
- return quote_spanned ! {
204
- input . span ( ) => compile_error! ( "Unions cannot be used as labels." ) ;
205
- }
206
- . into ( ) ;
267
+ let err_msg = format ! (
268
+ "Unions cannot be used as labels, unless marked with `#[{attr_name}(intern)]`."
269
+ ) ;
270
+ return Err ( syn :: Error :: new ( input . span ( ) , err_msg ) ) ;
207
271
}
208
272
} ;
209
273
210
- ( quote ! {
274
+ Ok ( quote ! {
211
275
impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
212
- fn as_str( & self ) -> & ' static str {
213
- #as_str
276
+ #[ inline]
277
+ fn data( & self ) -> u64 {
278
+ #data
279
+ }
280
+ fn fmt( data: u64 , f: & mut :: std:: fmt:: Formatter ) -> :: std:: fmt:: Result {
281
+ #fmt
214
282
}
215
283
}
216
284
} )
217
- . into ( )
218
285
}
0 commit comments