24
24
use heck:: ToUpperCamelCase ;
25
25
use proc_macro:: TokenStream ;
26
26
use quote:: quote;
27
- use syn:: parse:: Parse ;
28
- use syn:: { parse_macro_input, Data , DataStruct , DeriveInput } ;
27
+ use syn:: punctuated:: Punctuated ;
28
+ use syn:: {
29
+ parse_macro_input, Data , DataStruct , DeriveInput , Expr , ExprLit , Lit , Meta , MetaNameValue ,
30
+ Token ,
31
+ } ;
29
32
30
33
/// Generates a delegating `NetworkBehaviour` implementation for the struct this is used for. See
31
34
/// the trait documentation for better description.
@@ -48,8 +51,13 @@ fn build(ast: &DeriveInput) -> TokenStream {
48
51
fn build_struct ( ast : & DeriveInput , data_struct : & DataStruct ) -> TokenStream {
49
52
let name = & ast. ident ;
50
53
let ( _, ty_generics, where_clause) = ast. generics . split_for_impl ( ) ;
51
- let prelude_path = parse_attribute_value_by_key :: < syn:: Path > ( ast, "prelude" )
52
- . unwrap_or_else ( || syn:: parse_quote! { :: libp2p:: swarm:: derive_prelude } ) ;
54
+ let BehaviourAttributes {
55
+ prelude_path,
56
+ user_specified_out_event,
57
+ } = match parse_attributes ( ast) {
58
+ Ok ( attrs) => attrs,
59
+ Err ( e) => return e,
60
+ } ;
53
61
54
62
let multiaddr = quote ! { #prelude_path:: Multiaddr } ;
55
63
let trait_to_impl = quote ! { #prelude_path:: NetworkBehaviour } ;
@@ -91,10 +99,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
91
99
// If we find a `#[behaviour(out_event = "Foo")]` attribute on the
92
100
// struct, we set `Foo` as the out event. If not, the `OutEvent` is
93
101
// generated.
94
- let user_provided_out_event_name =
95
- parse_attribute_value_by_key :: < syn:: Type > ( ast, "out_event" ) ;
96
-
97
- match user_provided_out_event_name {
102
+ match user_specified_out_event {
98
103
// User provided `OutEvent`.
99
104
Some ( name) => {
100
105
let definition = None ;
@@ -111,7 +116,8 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
111
116
// User did not provide `OutEvent`. Generate it.
112
117
None => {
113
118
let enum_name_str = ast. ident . to_string ( ) + "Event" ;
114
- let enum_name: syn:: Type = syn:: parse_str ( & enum_name_str) . unwrap ( ) ;
119
+ let enum_name: syn:: Type =
120
+ syn:: parse_str ( & enum_name_str) . expect ( "ident + `Event` is a valid type" ) ;
115
121
let definition = {
116
122
let fields = data_struct. fields . iter ( ) . map ( |field| {
117
123
let variant: syn:: Variant = syn:: parse_str (
@@ -122,7 +128,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
122
128
. to_string ( )
123
129
. to_upper_camel_case ( ) ,
124
130
)
125
- . unwrap ( ) ;
131
+ . expect ( "uppercased field name to be a valid enum variant" ) ;
126
132
let ty = & field. ty ;
127
133
( variant, ty)
128
134
} ) ;
@@ -680,7 +686,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
680
686
& field
681
687
. to_string ( )
682
688
. to_upper_camel_case ( )
683
- ) . unwrap ( ) ;
689
+ ) . expect ( "uppercased field name to be a valid enum variant name" ) ;
684
690
quote ! { #out_event_name:: #event_variant( event) }
685
691
} else {
686
692
quote ! { event. into( ) }
@@ -842,41 +848,83 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream {
842
848
final_quote. into ( )
843
849
}
844
850
851
+ struct BehaviourAttributes {
852
+ prelude_path : syn:: Path ,
853
+ user_specified_out_event : Option < syn:: Type > ,
854
+ }
855
+
845
856
/// Parses the `value` of a key=value pair in the `#[behaviour]` attribute into the requested type.
846
- ///
847
- /// Only `String` values are supported, e.g. `#[behaviour(foo="bar")]`.
848
- fn parse_attribute_value_by_key < T > ( ast : & DeriveInput , key : & str ) -> Option < T >
849
- where
850
- T : Parse ,
851
- {
852
- ast. attrs
857
+ fn parse_attributes ( ast : & DeriveInput ) -> Result < BehaviourAttributes , TokenStream > {
858
+ let mut attributes = BehaviourAttributes {
859
+ prelude_path : syn:: parse_quote! { :: libp2p:: swarm:: derive_prelude } ,
860
+ user_specified_out_event : None ,
861
+ } ;
862
+
863
+ for attr in ast
864
+ . attrs
853
865
. iter ( )
854
- . filter_map ( get_meta_items)
855
- . flatten ( )
856
- . filter_map ( |meta_item| {
857
- if let syn:: NestedMeta :: Meta ( syn:: Meta :: NameValue ( ref m) ) = meta_item {
858
- if m. path . is_ident ( key) {
859
- if let syn:: Lit :: Str ( ref s) = m. lit {
860
- return Some ( syn:: parse_str ( & s. value ( ) ) . unwrap ( ) ) ;
866
+ . filter ( |attr| attr. path ( ) . is_ident ( "behaviour" ) )
867
+ {
868
+ let nested = attr
869
+ . parse_args_with ( Punctuated :: < Meta , Token ! [ , ] > :: parse_terminated)
870
+ . expect ( "`parse_args_with` never fails when parsing nested meta" ) ;
871
+
872
+ for meta in nested {
873
+ if meta. path ( ) . is_ident ( "prelude" ) {
874
+ match meta {
875
+ Meta :: Path ( _) => unimplemented ! ( ) ,
876
+ Meta :: List ( _) => unimplemented ! ( ) ,
877
+ Meta :: NameValue ( MetaNameValue {
878
+ value :
879
+ Expr :: Lit ( ExprLit {
880
+ lit : Lit :: Str ( s) , ..
881
+ } ) ,
882
+ ..
883
+ } ) => {
884
+ attributes. prelude_path = syn:: parse_str ( & s. value ( ) ) . unwrap ( ) ;
885
+ }
886
+ Meta :: NameValue ( name_value) => {
887
+ return Err ( syn:: Error :: new_spanned (
888
+ name_value. value ,
889
+ "`prelude` value must be a quoted path" ,
890
+ )
891
+ . to_compile_error ( )
892
+ . into ( ) ) ;
861
893
}
862
894
}
895
+
896
+ continue ;
863
897
}
864
- None
865
- } )
866
- . next ( )
867
- }
868
898
869
- fn get_meta_items ( attr : & syn:: Attribute ) -> Option < Vec < syn:: NestedMeta > > {
870
- if attr. path . segments . len ( ) == 1 && attr. path . segments [ 0 ] . ident == "behaviour" {
871
- match attr. parse_meta ( ) {
872
- Ok ( syn:: Meta :: List ( ref meta) ) => Some ( meta. nested . iter ( ) . cloned ( ) . collect ( ) ) ,
873
- Ok ( _) => None ,
874
- Err ( e) => {
875
- eprintln ! ( "error parsing attribute metadata: {e}" ) ;
876
- None
899
+ if meta. path ( ) . is_ident ( "out_event" ) {
900
+ match meta {
901
+ Meta :: Path ( _) => unimplemented ! ( ) ,
902
+ Meta :: List ( _) => unimplemented ! ( ) ,
903
+
904
+ Meta :: NameValue ( MetaNameValue {
905
+ value :
906
+ Expr :: Lit ( ExprLit {
907
+ lit : Lit :: Str ( s) , ..
908
+ } ) ,
909
+ ..
910
+ } ) => {
911
+ attributes. user_specified_out_event =
912
+ Some ( syn:: parse_str ( & s. value ( ) ) . unwrap ( ) ) ;
913
+ }
914
+ Meta :: NameValue ( name_value) => {
915
+ return Err ( syn:: Error :: new_spanned (
916
+ name_value. value ,
917
+ "`out_event` value must be a quoted type" ,
918
+ )
919
+ . to_compile_error ( )
920
+ . into ( ) ) ;
921
+ }
922
+ }
923
+
924
+ continue ;
877
925
}
878
926
}
879
- } else {
880
- None
881
927
}
928
+
929
+ Ok ( attributes)
882
930
}
0 commit comments