@@ -2,6 +2,8 @@ use crate::format::{self, Format};
2
2
3
3
use proc_macro2:: { Span , TokenStream } ;
4
4
use syn;
5
+ use syn:: { ExprPath , ExprBinary , ExprUnary , Expr } ;
6
+ use quote:: ToTokens ;
5
7
6
8
#[ derive( Debug ) ]
7
9
pub enum Protocol {
@@ -13,6 +15,35 @@ pub enum Protocol {
13
15
prefix_subfield_names : Vec < syn:: Ident > ,
14
16
} ,
15
17
FixedLength ( usize ) ,
18
+ SkipIf ( SkipExpression ) ,
19
+ }
20
+
21
+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
22
+ pub enum SkipExpression {
23
+ PathExp ( ExprPath ) ,
24
+ BinaryExp ( ExprBinary ) ,
25
+ UnaryExp ( ExprUnary ) ,
26
+ }
27
+
28
+ impl SkipExpression {
29
+ pub fn parse_from ( exp : & str ) -> SkipExpression {
30
+ let expr = syn:: parse_str :: < Expr > ( exp) . unwrap ( ) ;
31
+
32
+ match expr {
33
+ Expr :: Binary ( e) => SkipExpression :: BinaryExp ( e) ,
34
+ Expr :: Unary ( e) => SkipExpression :: UnaryExp ( e) ,
35
+ Expr :: Path ( e) => SkipExpression :: PathExp ( e) ,
36
+ _ => panic ! ( "Unexpected skip expression" )
37
+ }
38
+ }
39
+
40
+ pub fn to_token_stream ( & self ) -> TokenStream {
41
+ match self {
42
+ SkipExpression :: PathExp ( e) => e. to_token_stream ( ) ,
43
+ SkipExpression :: BinaryExp ( e) => e. to_token_stream ( ) ,
44
+ SkipExpression :: UnaryExp ( ref e) => e. to_token_stream ( )
45
+ }
46
+ }
16
47
}
17
48
18
49
#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
@@ -37,7 +68,7 @@ pub fn repr(attrs: &[syn::Attribute]) -> Option<syn::Ident> {
37
68
}
38
69
39
70
pub fn protocol ( attrs : & [ syn:: Attribute ] )
40
- -> Option < Protocol > {
71
+ -> Option < Protocol > {
41
72
let meta_list = attrs. iter ( ) . filter_map ( |attr| match attr. parse_meta ( ) {
42
73
Ok ( syn:: Meta :: List ( meta_list) ) => {
43
74
if meta_list. path . get_ident ( ) == Some ( & syn:: Ident :: new ( "protocol" , proc_macro2:: Span :: call_site ( ) ) ) {
@@ -46,17 +77,31 @@ pub fn protocol(attrs: &[syn::Attribute])
46
77
// Unrelated attribute.
47
78
None
48
79
}
49
- } ,
80
+ }
50
81
_ => None ,
51
82
} ) . next ( ) ;
52
83
53
- let meta_list: syn:: MetaList = if let Some ( meta_list) = meta_list { meta_list } else { return None } ;
84
+ let meta_list: syn:: MetaList = if let Some ( meta_list) = meta_list { meta_list } else { return None ; } ;
54
85
let mut nested_metas = meta_list. nested . into_iter ( ) ;
55
86
56
87
match nested_metas. next ( ) {
57
88
Some ( syn:: NestedMeta :: Meta ( syn:: Meta :: List ( nested_list) ) ) => {
58
89
match & nested_list. path . get_ident ( ) . expect ( "meta is not an ident" ) . to_string ( ) [ ..] {
59
90
// #[protocol(length_prefix(<kind>(<prefix field name>)))]
91
+ "skip_if" => {
92
+ let expression = expect:: meta_list:: single_element ( nested_list) . unwrap ( ) ;
93
+ let expression = match expression {
94
+ syn:: NestedMeta :: Lit ( syn:: Lit :: Str ( s) ) => {
95
+ SkipExpression :: parse_from ( & s. value ( ) )
96
+ }
97
+ syn:: NestedMeta :: Meta ( syn:: Meta :: Path ( path) ) => {
98
+ todo ! ( "Path literal not implemented yet" )
99
+ }
100
+ _ => panic ! ( "OH no! ! " )
101
+ } ;
102
+
103
+ Some ( Protocol :: SkipIf ( expression) )
104
+ }
60
105
"fixed_length" => {
61
106
let nested_list = expect:: meta_list:: single_literal ( nested_list)
62
107
. expect ( "expected a nested list" ) ;
@@ -71,7 +116,7 @@ pub fn protocol(attrs: &[syn::Attribute])
71
116
}
72
117
"length_prefix" => {
73
118
let nested_list = expect:: meta_list:: nested_list ( nested_list)
74
- . expect ( "expected a nested list" ) ;
119
+ . expect ( "expected a nested list" ) ;
75
120
let prefix_kind = match & nested_list. path . get_ident ( ) . expect ( "nested list is not an ident" ) . to_string ( ) [ ..] {
76
121
"bytes" => LengthPrefixKind :: Bytes ,
77
122
"elements" => LengthPrefixKind :: Elements ,
@@ -82,9 +127,9 @@ pub fn protocol(attrs: &[syn::Attribute])
82
127
let ( prefix_field_name, prefix_subfield_names) = match length_prefix_expr {
83
128
syn:: NestedMeta :: Lit ( syn:: Lit :: Str ( s) ) => {
84
129
let mut parts: Vec < _ > = s. value ( )
85
- . split ( "." )
86
- . map ( |s| syn:: Ident :: new ( s, Span :: call_site ( ) ) )
87
- . collect ( ) ;
130
+ . split ( "." )
131
+ . map ( |s| syn:: Ident :: new ( s, Span :: call_site ( ) ) )
132
+ . collect ( ) ;
88
133
89
134
if parts. len ( ) < 1 {
90
135
panic ! ( "there must be at least one field mentioned" ) ;
@@ -94,7 +139,7 @@ pub fn protocol(attrs: &[syn::Attribute])
94
139
let subfield_idents = parts. into_iter ( ) . collect ( ) ;
95
140
96
141
( field_ident, subfield_idents)
97
- } ,
142
+ }
98
143
syn:: NestedMeta :: Meta ( syn:: Meta :: Path ( path) ) => match path. get_ident ( ) {
99
144
Some ( field_ident) => ( field_ident. clone ( ) , Vec :: new ( ) ) ,
100
145
None => panic ! ( "path is not an ident" ) ,
@@ -103,15 +148,15 @@ pub fn protocol(attrs: &[syn::Attribute])
103
148
} ;
104
149
105
150
Some ( Protocol :: LengthPrefix { kind : prefix_kind, prefix_field_name, prefix_subfield_names } )
106
- } ,
151
+ }
107
152
"discriminator" => {
108
153
let literal = expect:: meta_list:: single_literal ( nested_list)
109
- . expect ( "expected a single literal" ) ;
154
+ . expect ( "expected a single literal" ) ;
110
155
Some ( Protocol :: Discriminator ( literal) )
111
- } ,
156
+ }
112
157
name => panic ! ( "#[protocol({})] is not valid" , name) ,
113
158
}
114
- } ,
159
+ }
115
160
Some ( syn:: NestedMeta :: Meta ( syn:: Meta :: NameValue ( name_value) ) ) => {
116
161
match name_value. path . get_ident ( ) {
117
162
Some ( ident) => {
@@ -198,3 +243,32 @@ mod attribute {
198
243
}
199
244
}
200
245
246
+ #[ cfg( test) ]
247
+ mod test {
248
+ use crate :: attr:: SkipExpression ;
249
+
250
+ #[ test]
251
+ fn should_parse_skip_expression ( ) {
252
+ let binary = "a == b" ;
253
+ let parse_result = SkipExpression :: parse_from ( binary) ;
254
+ assert ! ( matches!( parse_result, SkipExpression :: BinaryExp ( _) ) ) ;
255
+
256
+ let unary = "!b" ;
257
+ let parse_result = SkipExpression :: parse_from ( unary) ;
258
+ assert ! ( matches!( parse_result, SkipExpression :: UnaryExp ( _) ) ) ;
259
+
260
+ let path = "hello" ;
261
+ let parse_result = SkipExpression :: parse_from ( path) ;
262
+ assert ! ( matches!( parse_result, SkipExpression :: PathExp ( _) ) ) ;
263
+ }
264
+
265
+ #[ test]
266
+ fn should_convert_expression_to_token ( ) {
267
+ let binary = "a == b" ;
268
+ let parse_result = SkipExpression :: parse_from ( binary) ;
269
+ let tokens = parse_result. to_token_stream ( ) ;
270
+ let expression = quote ! { #tokens } ;
271
+ assert_eq ! ( expression. to_string( ) , "a == b" ) ;
272
+ }
273
+ }
274
+
0 commit comments