@@ -50,17 +50,20 @@ use proc_macro2::Span;
50
50
use quote:: __rt:: TokenStream as Tokens ;
51
51
use quote:: quote;
52
52
use syn:: Attribute ;
53
+ use syn:: Binding ;
53
54
use syn:: Data ;
54
55
use syn:: DeriveInput ;
55
56
use syn:: Fields ;
56
57
use syn:: GenericParam ;
57
58
use syn:: Generics ;
58
- use syn:: Lit ;
59
- use syn:: Meta ;
60
- use syn:: NestedMeta ;
59
+ use syn:: parenthesized ;
60
+ use syn:: parse :: Parse ;
61
+ use syn:: parse :: ParseStream ;
61
62
use syn:: parse2;
62
63
use syn:: punctuated:: Punctuated ;
63
64
use syn:: token:: Comma ;
65
+ use syn:: token:: Eq ;
66
+ use syn:: Type ;
64
67
use syn:: TypeGenerics ;
65
68
use syn:: WhereClause ;
66
69
use syn:: WherePredicate ;
@@ -69,7 +72,7 @@ use syn::WherePredicate;
69
72
/// A type indicating whether or not to create a default implementation of Type::new().
70
73
type New = Option < ( ) > ;
71
74
/// A type representing an event type to parametrize a widget with.
72
- type Event = Option < String > ;
75
+ type Event = Option < Type > ;
73
76
74
77
75
78
/// The error type used internally by this module.
@@ -185,57 +188,81 @@ fn parse_attributes(attributes: &[Attribute]) -> Result<(New, Event)> {
185
188
}
186
189
187
190
/// Parse a single item in a #[gui(list...)] attribute list.
188
- fn parse_gui_attribute ( item : & NestedMeta ) -> Result < ( New , Event ) > {
189
- match * item {
190
- NestedMeta :: Meta ( ref meta_item) => {
191
- match * meta_item {
192
- Meta :: NameValue ( ref name_val) if name_val. ident == "Event" => {
193
- match name_val. lit {
194
- Lit :: Str ( ref string) => Ok ( ( None , Some ( string. value ( ) ) ) ) ,
195
- _ => Ok ( ( None , None ) ) ,
196
- }
197
- } ,
198
- Meta :: Word ( ref ident) if ident == "default_new" => Ok ( ( Some ( ( ) ) , None ) ) ,
199
- _ => Err ( Error :: from ( format ! ( "unsupported attribute: {}" , meta_item. name( ) ) ) ) ,
191
+ fn parse_gui_attribute ( item : Attr ) -> Result < ( New , Event ) > {
192
+ match item {
193
+ Attr :: Ident ( ref ident) if ident == "default_new" => {
194
+ Ok ( ( Some ( ( ) ) , None ) )
195
+ } ,
196
+ Attr :: Binding ( binding) => {
197
+ // Unfortunately we can't use a pattern guard here. See issue
198
+ // https://github.com/rust-lang/rust/issues/15287 for more
199
+ // details.
200
+ if binding. ident == "Event" {
201
+ Ok ( ( None , Some ( binding. ty ) ) )
202
+ } else {
203
+ Err ( Error :: from ( "encountered unknown attribute" ) )
200
204
}
201
205
} ,
202
- NestedMeta :: Literal ( _ ) => Err ( Error :: from ( "unsupported literal " ) ) ,
206
+ _ => Err ( Error :: from ( "encountered unknown attribute " ) ) ,
203
207
}
204
208
}
205
209
206
210
/// Parse a #[gui(list...)] attribute list.
207
- fn parse_gui_attributes ( list : & Punctuated < NestedMeta , Comma > ) -> Result < ( New , Event ) > {
211
+ fn parse_gui_attributes ( list : AttrList ) -> Result < ( New , Event ) > {
208
212
let mut new = None ;
209
213
let mut event = None ;
210
214
211
- for item in list {
215
+ for item in list. 0 {
212
216
let ( this_new, this_event) = parse_gui_attribute ( item) ?;
213
217
new = this_new. or ( new) ;
214
218
event = this_event. or ( event) ;
215
219
}
216
220
Ok ( ( new, event) )
217
221
}
218
222
219
- /// Parse a single attribute, e.g., #[GuiType = "Widget"].
220
- // TODO: Once https://github.com/rust-lang/rust/pull/57367 lands in
221
- // stable we should migrate to using the actual type and not a
222
- // textual representation of it.
223
+
224
+ /// An attribute list representing an syn::Attribute::tts.
225
+ struct AttrList ( Punctuated < Attr , Comma > ) ;
226
+
227
+ impl Parse for AttrList {
228
+ fn parse ( input : ParseStream < ' _ > ) -> syn:: Result < Self > {
229
+ let content;
230
+ let _ = parenthesized ! ( content in input) ;
231
+ let list = content. parse_terminated ( Attr :: parse) ?;
232
+
233
+ Ok ( Self ( list) )
234
+ }
235
+ }
236
+
237
+
238
+ enum Attr {
239
+ Ident ( Ident ) ,
240
+ Binding ( Binding ) ,
241
+ }
242
+
243
+ impl Parse for Attr {
244
+ fn parse ( input : ParseStream < ' _ > ) -> syn:: Result < Self > {
245
+ if input. peek2 ( Eq ) {
246
+ let bind = input. parse :: < Binding > ( ) ?;
247
+ Ok ( Attr :: Binding ( bind) )
248
+ } else {
249
+ input. parse :: < Ident > ( ) . map ( |ident| Attr :: Ident ( ident) )
250
+ }
251
+ }
252
+ }
253
+
254
+
255
+ /// Parse a single attribute, e.g., #[Event = MyEvent].
223
256
fn parse_attribute ( attribute : & Attribute ) -> Result < ( New , Event ) > {
224
- // We don't care about the other meta data elements, inner/outer,
225
- // doc/non-doc, it's all fine by us.
226
-
227
- match attribute. interpret_meta ( ) {
228
- Some ( x) => {
229
- match x {
230
- Meta :: List ( ref list) if list. ident == "gui" => {
231
- // Here we have found an attribute of the form #[gui(xxx, yyy,
232
- // ...)]. Parse the inner list.
233
- parse_gui_attributes ( & list. nested )
234
- } ,
235
- _ => Ok ( ( None , None ) ) ,
236
- }
237
- } ,
238
- None => Ok ( ( None , None ) ) ,
257
+ if attribute. path . is_ident ( "gui" ) {
258
+ let tokens = attribute. tts . clone ( ) ;
259
+ let attr = parse2 :: < AttrList > ( tokens) . or_else ( |err| {
260
+ Err ( format ! ( "unable to parse attributes: {:?}" , err) )
261
+ } ) ?;
262
+
263
+ parse_gui_attributes ( attr)
264
+ } else {
265
+ Ok ( ( None , None ) )
239
266
}
240
267
}
241
268
@@ -353,12 +380,12 @@ fn expand_widget_trait(event: &Event, input: &DeriveInput) -> Tokens {
353
380
let generic = event. is_none ( ) ;
354
381
let ( generics, ty_generics, where_clause) = split_for_impl ( & input. generics , generic) ;
355
382
356
- let event = if let Some ( event) = event {
357
- Ident :: new ( & event , Span :: call_site ( ) )
383
+ let widget = if let Some ( event) = event {
384
+ quote ! { :: gui :: Widget <#event> }
358
385
} else {
359
- Ident :: new ( "__E" , Span :: call_site ( ) )
386
+ let event = Ident :: new ( "__E" , Span :: call_site ( ) ) ;
387
+ quote ! { :: gui:: Widget <#event> }
360
388
} ;
361
- let widget = quote ! { :: gui:: Widget <#event> } ;
362
389
363
390
quote ! {
364
391
impl #generics #widget for #name #ty_generics #where_clause {
@@ -382,7 +409,7 @@ fn expand_widget_trait(event: &Event, input: &DeriveInput) -> Tokens {
382
409
/// # use gui_derive::Widget;
383
410
/// # type Event = ();
384
411
/// # #[derive(Debug, Widget)]
385
- /// # #[gui(Event = " Event" )]
412
+ /// # #[gui(Event = Event)]
386
413
/// # struct TestWidget {
387
414
/// # id: gui::Id,
388
415
/// # }
@@ -470,12 +497,12 @@ fn expand_handleable_trait(event: &Event, input: &DeriveInput) -> Tokens {
470
497
let generic = event. is_none ( ) ;
471
498
let ( generics, ty_generics, where_clause) = split_for_impl ( & input. generics , generic) ;
472
499
473
- let event = if let Some ( event) = event {
474
- Ident :: new ( & event , Span :: call_site ( ) )
500
+ let handleable = if let Some ( event) = event {
501
+ quote ! { :: gui :: Handleable <#event> }
475
502
} else {
476
- Ident :: new ( "__E" , Span :: call_site ( ) )
503
+ let event = Ident :: new ( "__E" , Span :: call_site ( ) ) ;
504
+ quote ! { :: gui:: Handleable <#event> }
477
505
} ;
478
- let handleable = quote ! { :: gui:: Handleable <#event> } ;
479
506
480
507
quote ! {
481
508
impl #generics #handleable for #name #ty_generics #where_clause { }
@@ -515,26 +542,49 @@ mod tests {
515
542
#[ test]
516
543
fn custom_event ( ) {
517
544
let tokens = quote ! {
518
- #[ gui( Event = " FooBarBazEvent" ) ]
545
+ #[ gui( Event = FooBarBazEvent ) ]
519
546
struct Bar { }
520
547
} ;
521
548
522
549
let input = parse2 :: < DeriveInput > ( tokens) . unwrap ( ) ;
523
550
let ( new, event) = parse_attributes ( & input. attrs ) . unwrap ( ) ;
524
551
assert_eq ! ( new, None ) ;
525
- assert_eq ! ( event, Some ( "FooBarBazEvent" . to_string( ) ) ) ;
552
+
553
+ let tokens = quote ! { FooBarBazEvent } ;
554
+ let foobar = parse2 :: < Type > ( tokens) . unwrap ( ) ;
555
+ assert_eq ! ( event, Some ( foobar) ) ;
556
+ }
557
+
558
+ #[ test]
559
+ fn default_new_and_event_with_ignore ( ) {
560
+ let tokens = quote ! {
561
+ #[ allow( an_attribute_to_be_ignored) ]
562
+ #[ gui( default_new, Event = ( ) ) ]
563
+ struct Baz { }
564
+ } ;
565
+
566
+ let input = parse2 :: < DeriveInput > ( tokens) . unwrap ( ) ;
567
+ let ( new, event) = parse_attributes ( & input. attrs ) . unwrap ( ) ;
568
+ assert_eq ! ( new, Some ( ( ) ) ) ;
569
+
570
+ let tokens = quote ! { ( ) } ;
571
+ let parens = parse2 :: < Type > ( tokens) . unwrap ( ) ;
572
+ assert_eq ! ( event, Some ( parens) ) ;
526
573
}
527
574
528
575
#[ test]
529
576
fn last_event_type_takes_precedence ( ) {
530
577
let tokens = quote ! {
531
- #[ gui( Event = " Event1" ) ]
532
- #[ gui( Event = " Event2" ) ]
578
+ #[ gui( Event = Event1 ) ]
579
+ #[ gui( Event = Event2 ) ]
533
580
struct Foo { }
534
581
} ;
535
582
536
583
let input = parse2 :: < DeriveInput > ( tokens) . unwrap ( ) ;
537
584
let ( _, event) = parse_attributes ( & input. attrs ) . unwrap ( ) ;
538
- assert_eq ! ( event. as_ref( ) . map( String :: as_str) , Some ( "Event2" ) ) ;
585
+
586
+ let tokens = quote ! { Event2 } ;
587
+ let event2 = parse2 :: < Type > ( tokens) . unwrap ( ) ;
588
+ assert_eq ! ( event, Some ( event2) ) ;
539
589
}
540
590
}
0 commit comments