@@ -5,7 +5,7 @@ use proc_macro::TokenStream;
5
5
use proc_macro2:: TokenStream as TokenStream2 ;
6
6
use quote:: format_ident;
7
7
use quote:: { quote, quote_spanned} ;
8
- use std:: str :: FromStr ;
8
+ use std:: collections :: HashMap ;
9
9
use syn:: ext:: IdentExt ;
10
10
use syn:: parenthesized;
11
11
use syn:: parse:: Parse ;
@@ -49,7 +49,7 @@ impl Parse for PropsMacroInput {
49
49
_ => {
50
50
return Err ( syn:: Error :: new (
51
51
derive_input. span ( ) ,
52
- "props can only be derived on structs" ,
52
+ "Properties can only be derived on structs" ,
53
53
) )
54
54
}
55
55
} ;
@@ -76,12 +76,6 @@ impl std::convert::From<Option<syn::Expr>> for MaybeCustomFn {
76
76
}
77
77
78
78
enum PropAttr {
79
- // ident
80
- Flag ( & ' static str ) ,
81
-
82
- // path
83
- FlagPath ( syn:: Path ) ,
84
-
85
79
// builder(required_params).parameter(value)
86
80
// becomes
87
81
// Builder(Punctuated(required_params), Optionals(TokenStream))
@@ -94,62 +88,33 @@ enum PropAttr {
94
88
// ident = expr
95
89
Type ( syn:: Type ) ,
96
90
91
+ // This will get translated from `ident = value` to `.ident(value)`
92
+ // and will get appended after the `builder(...)` call.
93
+ // ident [= expr]
94
+ BuilderField ( ( syn:: Ident , Option < syn:: Expr > ) ) ,
95
+
97
96
// ident = ident
98
97
Member ( syn:: Ident ) ,
99
98
100
99
// ident = "literal"
101
100
Name ( syn:: LitStr ) ,
102
- Nick ( syn:: LitStr ) ,
103
- Blurb ( syn:: LitStr ) ,
104
101
}
105
102
106
- const FLAGS : [ & str ; 16 ] = [
107
- "readable" ,
108
- "writable" ,
109
- "readwrite" ,
110
- "construct" ,
111
- "construct_only" ,
112
- "lax_validation" ,
113
- "user_1" ,
114
- "user_2" ,
115
- "user_3" ,
116
- "user_4" ,
117
- "user_5" ,
118
- "user_6" ,
119
- "user_7" ,
120
- "user_8" ,
121
- "explicit_notify" ,
122
- "deprecated" ,
123
- ] ;
124
103
impl Parse for PropAttr {
125
104
fn parse ( input : syn:: parse:: ParseStream ) -> syn:: Result < Self > {
126
105
let name = input. call ( syn:: Ident :: parse_any) ?;
127
106
let name_str = name. to_string ( ) ;
128
107
129
108
let res = if input. peek ( Token ! [ =] ) {
130
109
let _assign_token: Token ! [ =] = input. parse ( ) ?;
131
- if input. peek ( syn:: LitStr ) {
132
- let lit: syn:: LitStr = input. parse ( ) ?;
133
- // name = "literal"
134
- match & * name_str {
135
- "name" => PropAttr :: Name ( lit) ,
136
- "nick" => PropAttr :: Nick ( lit) ,
137
- "blurb" => PropAttr :: Blurb ( lit) ,
138
- _ => {
139
- panic ! ( "Invalid attribute for property" )
140
- }
141
- }
142
- } else {
143
- // name = expr | type | ident
144
- match & * name_str {
145
- "get" => PropAttr :: Get ( Some ( input. parse ( ) ?) ) ,
146
- "set" => PropAttr :: Set ( Some ( input. parse ( ) ?) ) ,
147
- "type" => PropAttr :: Type ( input. parse ( ) ?) ,
148
- "member" => PropAttr :: Member ( input. parse ( ) ?) ,
149
- _ => {
150
- panic ! ( "Invalid attribute for property" )
151
- }
152
- }
110
+ // name = expr | type | ident
111
+ match & * name_str {
112
+ "name" => PropAttr :: Name ( input. parse ( ) ?) ,
113
+ "get" => PropAttr :: Get ( Some ( input. parse ( ) ?) ) ,
114
+ "set" => PropAttr :: Set ( Some ( input. parse ( ) ?) ) ,
115
+ "type" => PropAttr :: Type ( input. parse ( ) ?) ,
116
+ "member" => PropAttr :: Member ( input. parse ( ) ?) ,
117
+ _ => PropAttr :: BuilderField ( ( name, Some ( input. parse ( ) ?) ) ) ,
153
118
}
154
119
} else if input. peek ( syn:: token:: Paren ) {
155
120
match & * name_str {
@@ -160,31 +125,29 @@ impl Parse for PropAttr {
160
125
let rest: TokenStream2 = input. parse ( ) ?;
161
126
PropAttr :: Builder ( required, rest)
162
127
}
163
- _ => panic ! ( "Unsupported attribute list {name_str}(...)" ) ,
128
+ _ => {
129
+ return Err ( syn:: Error :: new (
130
+ name. span ( ) ,
131
+ format ! ( "Unsupported attribute list {name_str}(...)" ) ,
132
+ ) )
133
+ }
164
134
}
165
- } else if input. peek ( Token ! [ :: ] ) {
166
- let mut p: syn:: Path = input. parse ( ) ?;
167
- p. segments . insert (
168
- 0 ,
169
- syn:: PathSegment {
170
- ident : format_ident ! ( "{}" , name) ,
171
- arguments : syn:: PathArguments :: None ,
172
- } ,
173
- ) ;
174
- PropAttr :: FlagPath ( p)
175
135
} else {
176
136
// attributes with only the identifier
177
137
// name
178
138
match & * name_str {
179
139
"get" => PropAttr :: Get ( None ) ,
180
140
"set" => PropAttr :: Set ( None ) ,
181
- name => {
182
- if let Some ( flag) = FLAGS . iter ( ) . find ( |x| * x == & name) {
183
- PropAttr :: Flag ( flag)
184
- } else {
185
- panic ! ( "Invalid attribute for property" )
186
- }
141
+ "readwrite" | "read_only" | "write_only" => {
142
+ return Err ( syn:: Error :: new (
143
+ name. span ( ) ,
144
+ format ! (
145
+ "{name} is a flag managed by the Properties macro. \
146
+ Use `get` and `set` to manage read and write access to a property",
147
+ ) ,
148
+ ) )
187
149
}
150
+ _ => PropAttr :: BuilderField ( ( name, None ) ) ,
188
151
}
189
152
} ;
190
153
Ok ( res)
@@ -197,69 +160,73 @@ struct ReceivedAttrs {
197
160
set : Option < MaybeCustomFn > ,
198
161
ty : Option < syn:: Type > ,
199
162
member : Option < syn:: Ident > ,
200
- flags : Vec < & ' static str > ,
201
- flags_paths : Vec < syn:: Path > ,
202
163
name : Option < syn:: LitStr > ,
203
- nick : Option < syn:: LitStr > ,
204
- blurb : Option < syn:: LitStr > ,
205
164
builder : Option < ( Punctuated < syn:: Expr , Token ! [ , ] > , TokenStream2 ) > ,
165
+ builder_fields : HashMap < syn:: Ident , Option < syn:: Expr > > ,
206
166
}
207
167
impl ReceivedAttrs {
208
- fn new ( attrs : impl IntoIterator < Item = PropAttr > ) -> Self {
209
- attrs. into_iter ( ) . fold ( Self :: default ( ) , |mut this, attr| {
168
+ fn new (
169
+ attrs_span : & proc_macro2:: Span ,
170
+ attrs : impl IntoIterator < Item = PropAttr > ,
171
+ ) -> syn:: Result < Self > {
172
+ let this = attrs. into_iter ( ) . fold ( Self :: default ( ) , |mut this, attr| {
210
173
this. set_from_attr ( attr) ;
211
174
this
212
- } )
175
+ } ) ;
176
+
177
+ if this. get . is_none ( ) && this. set . is_none ( ) {
178
+ return Err ( syn:: Error :: new (
179
+ * attrs_span,
180
+ "No `get` or `set` specified: at least one is required." . to_string ( ) ,
181
+ ) ) ;
182
+ }
183
+ Ok ( this)
213
184
}
214
185
fn set_from_attr ( & mut self , attr : PropAttr ) {
215
186
match attr {
216
187
PropAttr :: Get ( some_fn) => self . get = Some ( some_fn. into ( ) ) ,
217
188
PropAttr :: Set ( some_fn) => self . set = Some ( some_fn. into ( ) ) ,
218
189
PropAttr :: Name ( lit) => self . name = Some ( lit) ,
219
- PropAttr :: Nick ( lit) => self . nick = Some ( lit) ,
220
- PropAttr :: Blurb ( lit) => self . blurb = Some ( lit) ,
221
190
PropAttr :: Type ( ty) => self . ty = Some ( ty) ,
222
191
PropAttr :: Member ( member) => self . member = Some ( member) ,
223
- PropAttr :: Flag ( flag) => self . flags . push ( flag) ,
224
- PropAttr :: FlagPath ( flag) => self . flags_paths . push ( flag) ,
225
192
PropAttr :: Builder ( required_params, optionals) => {
226
193
self . builder = Some ( ( required_params, optionals) )
227
194
}
195
+ PropAttr :: BuilderField ( ( ident, expr) ) => {
196
+ self . builder_fields . insert ( ident, expr) ;
197
+ }
228
198
}
229
199
}
230
200
}
201
+
202
+ // It's a cleaned up version of `ReceivedAttrs` where some missing attributes get a default,
203
+ // generated value.
231
204
struct PropDesc {
232
205
attrs_span : proc_macro2:: Span ,
233
206
field_ident : syn:: Ident ,
234
207
ty : syn:: Type ,
235
208
name : syn:: LitStr ,
236
- nick : Option < syn:: LitStr > ,
237
- blurb : Option < syn:: LitStr > ,
238
209
get : Option < MaybeCustomFn > ,
239
210
set : Option < MaybeCustomFn > ,
240
211
member : Option < syn:: Ident > ,
241
- flags : Vec < & ' static str > ,
242
- flags_paths : Vec < syn:: Path > ,
243
212
builder : Option < ( Punctuated < syn:: Expr , Token ! [ , ] > , TokenStream2 ) > ,
213
+ builder_fields : HashMap < syn:: Ident , Option < syn:: Expr > > ,
244
214
}
245
215
impl PropDesc {
246
216
fn new (
247
217
attrs_span : proc_macro2:: Span ,
248
218
field_ident : syn:: Ident ,
249
219
field_ty : syn:: Type ,
250
220
attrs : ReceivedAttrs ,
251
- ) -> Self {
221
+ ) -> syn :: Result < Self > {
252
222
let ReceivedAttrs {
253
223
get,
254
224
set,
255
225
ty,
256
226
member,
257
- flags,
258
227
name,
259
- nick,
260
- blurb,
261
228
builder,
262
- flags_paths ,
229
+ builder_fields ,
263
230
} = attrs;
264
231
265
232
// Fill needed, but missing, attributes with calculated default values
@@ -272,20 +239,17 @@ impl PropDesc {
272
239
let ty = ty. unwrap_or_else ( || field_ty. clone ( ) ) ;
273
240
274
241
// Now that everything is set and safe, return the final proprety description
275
- Self {
242
+ Ok ( Self {
276
243
attrs_span,
277
244
field_ident,
278
245
get,
279
246
set,
280
247
ty,
281
248
member,
282
- flags,
283
249
name,
284
- nick,
285
- blurb,
286
250
builder,
287
- flags_paths ,
288
- }
251
+ builder_fields ,
252
+ } )
289
253
}
290
254
}
291
255
@@ -294,52 +258,39 @@ fn expand_properties_fn(props: &[PropDesc]) -> TokenStream2 {
294
258
let crate_ident = crate_ident_new ( ) ;
295
259
let properties_build_phase = props. iter ( ) . map ( |prop| {
296
260
let PropDesc {
297
- ty,
298
- name,
299
- nick,
300
- blurb,
301
- builder,
302
- flags_paths,
303
- ..
261
+ ty, name, builder, ..
304
262
} = prop;
305
263
306
- let flags = {
307
- let write = prop. set . as_ref ( ) . map ( |_| quote ! ( WRITABLE ) ) ;
308
- let read = prop. get . as_ref ( ) . map ( |_| quote ! ( READABLE ) ) ;
309
-
310
- let flags_iter = [ write, read] . into_iter ( ) . flatten ( ) . chain (
311
- prop. flags
312
- . iter ( )
313
- . map ( |x| str:: to_uppercase ( x) )
314
- . map ( |f| TokenStream2 :: from_str ( & f) . unwrap ( ) ) ,
315
- ) ;
316
- quote ! ( #crate_ident:: ParamFlags :: empty( ) #( | #crate_ident:: ParamFlags :: #flags_iter) * #( | #flags_paths) * )
264
+ let rw_flags = match ( & prop. get , & prop. set ) {
265
+ ( Some ( _) , Some ( _) ) => quote ! ( . readwrite( ) ) ,
266
+ ( Some ( _) , None ) => quote ! ( . read_only( ) ) ,
267
+ ( None , Some ( _) ) => quote ! ( . write_only( ) ) ,
268
+ ( None , None ) => unreachable ! ( "No `get` or `set` specified" ) ,
317
269
} ;
318
270
319
271
let builder_call = builder
320
272
. as_ref ( )
321
273
. cloned ( )
322
- . map ( |( mut required_params, opts ) | {
274
+ . map ( |( mut required_params, chained_methods ) | {
323
275
let name_expr = syn:: ExprLit {
324
276
attrs : vec ! [ ] ,
325
277
lit : syn:: Lit :: Str ( name. to_owned ( ) ) ,
326
278
} ;
327
279
required_params. insert ( 0 , name_expr. into ( ) ) ;
328
280
let required_params = required_params. iter ( ) ;
329
281
330
- quote ! ( ( #( #required_params, ) * ) #opts )
282
+ quote ! ( ( #( #required_params, ) * ) #chained_methods )
331
283
} )
332
284
. unwrap_or ( quote ! ( ( #name) ) ) ;
333
285
334
- let build_nick = nick . as_ref ( ) . map ( |x | quote ! ( . nick ( #x ) ) ) ;
335
- let build_blurb = blurb . as_ref ( ) . map ( |x| quote ! ( . blurb ( #x ) ) ) ;
286
+ let builder_fields = prop . builder_fields . iter ( ) . map ( |( k , v ) | quote ! ( . #k ( #v ) ) ) ;
287
+
336
288
let span = prop. attrs_span ;
337
289
quote_spanned ! { span=>
338
290
<<#ty as #crate_ident:: Property >:: Value as #crate_ident:: HasParamSpec >
339
291
:: param_spec_builder( ) #builder_call
340
- #build_nick
341
- #build_blurb
342
- . flags( #flags)
292
+ #rw_flags
293
+ #( #builder_fields) *
343
294
. build( )
344
295
}
345
296
} ) ;
@@ -473,12 +424,12 @@ fn parse_fields(fields: syn::Fields) -> syn::Result<Vec<PropDesc>> {
473
424
let attrs = attrs. parse_args_with (
474
425
syn:: punctuated:: Punctuated :: < PropAttr , Token ! [ , ] > :: parse_terminated,
475
426
) ?;
476
- Ok ( PropDesc :: new (
427
+ PropDesc :: new (
477
428
span,
478
429
ident. as_ref ( ) . unwrap ( ) . clone ( ) ,
479
430
ty. clone ( ) ,
480
- ReceivedAttrs :: new ( attrs) ,
481
- ) )
431
+ ReceivedAttrs :: new ( & span , attrs) ? ,
432
+ )
482
433
} )
483
434
} )
484
435
. collect :: < syn:: Result < _ > > ( )
@@ -501,7 +452,8 @@ fn expand_getset_properties_impl(props: &[PropDesc]) -> TokenStream2 {
501
452
self . property:: <<#ty as #crate_ident:: Property >:: Value >( #name)
502
453
} )
503
454
} ) ;
504
- let setter = ( p. set . is_some ( ) && !p. flags . contains ( & "construct_only" ) ) . then ( || {
455
+ let is_construct_only = p. builder_fields . iter ( ) . any ( |( k, _) | * k == "construct_only" ) ;
456
+ let setter = ( p. set . is_some ( ) && !is_construct_only) . then ( || {
505
457
let ident = format_ident ! ( "set_{}" , ident) ;
506
458
quote ! ( pub fn #ident<' a>( & self , value: impl std:: borrow:: Borrow <<<#ty as #crate_ident:: Property >:: Value as #crate_ident:: HasParamSpec >:: SetValue >) {
507
459
self . set_property_from_value( #name, & :: std:: convert:: From :: from( std:: borrow:: Borrow :: borrow( & value) ) )
0 commit comments