1
1
use syn:: { spanned:: Spanned , Data , DeriveInput } ;
2
2
3
- use super :: { is_unknown, is_with} ;
3
+ use super :: { is_unknown, is_with, EnumType } ;
4
4
5
5
pub fn expand ( DeriveInput { ident, data, .. } : DeriveInput ) -> syn:: ItemImpl {
6
6
match data {
7
7
Data :: Struct ( data) => {
8
8
let is_named = data. fields . iter ( ) . any ( |field| field. ident . is_some ( ) ) ;
9
+ let names = data. fields . iter ( )
10
+ . enumerate ( )
11
+ . map ( |( idx, field) | match field. ident . as_ref ( ) {
12
+ Some ( name) => name. clone ( ) ,
13
+ None => {
14
+ let name = format ! ( "unit{idx}" ) ;
15
+ let name = syn:: Ident :: new ( & name, field. span ( ) ) ;
16
+ name
17
+ }
18
+ } )
19
+ . collect :: < Vec < _ > > ( ) ;
9
20
10
21
let fields = data. fields . iter ( )
11
- . enumerate ( )
12
- . map ( |( idx , field ) | -> syn:: Stmt {
22
+ . zip ( names . iter ( ) )
23
+ . map ( |( field , field_name ) | -> syn:: Stmt {
13
24
let ty = & field. ty ;
14
25
let value: syn:: Expr = match is_with ( & field. attrs ) {
15
26
Some ( with_type) => syn:: parse_quote!( <#with_type<#ty> as cbor4ii:: core:: dec:: Decode <' _>>:: decode( reader) ?. 0 ) ,
16
27
None => syn:: parse_quote!( <#ty as cbor4ii:: core:: dec:: Decode <' _>>:: decode( reader) ?)
17
28
} ;
18
29
19
- match field. ident . as_ref ( ) {
20
- Some ( name) => syn:: parse_quote!{
21
- let #name = #value;
22
- } ,
23
- None => {
24
- let name = format ! ( "unit{idx}" ) ;
25
- let name = syn:: Ident :: new ( & name, field. span ( ) ) ;
26
- syn:: parse_quote!{
27
- let #name = #value;
28
- }
29
- }
30
+ syn:: parse_quote!{
31
+ let #field_name = #value;
30
32
}
31
33
} ) ;
32
- let build_struct = data
33
- . fields
34
- . iter ( )
35
- . enumerate ( )
36
- . map ( |( idx, field) | -> syn:: FieldValue {
37
- match field. ident . as_ref ( ) {
38
- Some ( name) => syn:: parse_quote!( #name) ,
39
- None => {
40
- let name = format ! ( "unit{idx}" ) ;
41
- let name = syn:: Ident :: new ( & name, field. span ( ) ) ;
42
- syn:: parse_quote!( #name)
43
- }
44
- }
34
+ let build_struct: syn:: Expr = is_named
35
+ . then ( || syn:: parse_quote! {
36
+ #ident { #( #names) , * }
45
37
} )
46
- . collect :: < syn:: punctuated:: Punctuated < _ , syn:: Token ![ , ] > > ( ) ;
47
- let build_struct: syn:: Expr = if is_named {
48
- syn:: parse_quote! {
49
- #ident { #build_struct }
50
- }
51
- } else {
52
- syn:: parse_quote! {
53
- #ident ( #build_struct )
54
- }
55
- } ;
38
+ . unwrap_or_else ( || syn:: parse_quote! {
39
+ #ident ( #( #names) , * )
40
+ } ) ;
56
41
57
42
let count = data. fields . len ( ) ;
58
43
let head: Option < syn:: Expr > = ( count != 1 ) . then ( || {
@@ -76,97 +61,151 @@ pub fn expand(DeriveInput { ident, data, .. }: DeriveInput) -> syn::ItemImpl {
76
61
}
77
62
}
78
63
Data :: Enum ( data) => {
64
+ let enum_type = data. variants . iter ( )
65
+ . filter ( |v| !is_unknown ( & v. attrs ) )
66
+ . fold ( None , |mut sum, next| {
67
+ let ty = match & next. fields {
68
+ syn:: Fields :: Named ( _) => EnumType :: Struct ,
69
+ syn:: Fields :: Unnamed ( fields) if fields. unnamed . len ( ) == 1 => EnumType :: One ,
70
+ syn:: Fields :: Unit => EnumType :: Unit ,
71
+ syn:: Fields :: Unnamed ( _) => panic ! ( "more than 1 unnamed member field are not allowed" )
72
+ } ;
73
+ match ( * sum. get_or_insert ( ty) , ty) {
74
+ ( EnumType :: Struct , EnumType :: Struct )
75
+ | ( EnumType :: Struct , EnumType :: Unit )
76
+ | ( EnumType :: Unit , EnumType :: Unit )
77
+ | ( EnumType :: One , EnumType :: One )
78
+ => ( ) ,
79
+ ( EnumType :: Unit , EnumType :: One )
80
+ | ( EnumType :: One , EnumType :: Unit )
81
+ | ( _, EnumType :: Struct ) => sum = Some ( EnumType :: Struct ) ,
82
+ _ => panic ! ( "enum member types must be consistent: {:?} {:?}" , sum, ty) ,
83
+ }
84
+ sum
85
+ } ) ;
86
+ let enum_type = enum_type. expect ( "enum cannot be empty" ) ;
79
87
let mut iter = data. variants . iter ( ) . peekable ( ) ;
80
- let mut is_unit = None ;
81
-
82
- assert ! ( !data. variants. is_empty( ) , "empty enums are not allowed" ) ;
83
-
84
- let unknown_arm: Option < syn:: Arm > = if let Some ( unknown) =
85
- iter. next_if ( |variant| is_unknown ( & variant. attrs ) )
86
- {
87
- let name = & unknown. ident ;
88
- assert ! (
89
- unknown. discriminant. is_none( ) ,
90
- "custom discriminant unsupport"
91
- ) ;
92
- assert ! (
93
- is_with( & unknown. attrs) . is_none( ) ,
94
- "unknown member is not allowed with type"
95
- ) ;
96
-
97
- Some ( match & unknown. fields {
98
- syn:: Fields :: Unnamed ( fields) => match fields. unnamed . len ( ) {
99
- 1 => {
100
- is_unit = Some ( true ) ;
101
- syn:: parse_quote! {
102
- tag => #ident:: #name( tag) ,
88
+
89
+ let unknown_arm: Option < syn:: Arm > = iter. next_if ( |variant| is_unknown ( & variant. attrs ) )
90
+ . map ( |unknown| {
91
+ let name = & unknown. ident ;
92
+ assert ! (
93
+ unknown. discriminant. is_none( ) ,
94
+ "custom discriminant unsupport"
95
+ ) ;
96
+ assert ! (
97
+ is_with( & unknown. attrs) . is_none( ) ,
98
+ "unknown member is not allowed with type"
99
+ ) ;
100
+
101
+ match & unknown. fields {
102
+ syn:: Fields :: Unnamed ( fields) => match fields. unnamed . len ( ) {
103
+ 1 => {
104
+ assert_eq ! ( enum_type, EnumType :: Unit ) ;
105
+ syn:: parse_quote! {
106
+ tag => #ident:: #name( tag) ,
107
+ }
103
108
}
104
- }
105
- 2 => {
106
- is_unit = Some ( false ) ;
107
- let val_ty = & fields. unnamed [ 1 ] . ty ;
108
-
109
- syn:: parse_quote! {
110
- tag => {
111
- let val = <#val_ty as cbor4ii:: core:: dec:: Decode <' _>>:: decode( reader) ?;
112
- #ident:: #name( tag, val)
113
- } ,
109
+ 2 => {
110
+ assert_eq ! ( enum_type, EnumType :: One ) ;
111
+ let val_ty = & fields. unnamed [ 1 ] . ty ;
112
+ syn:: parse_quote! {
113
+ tag => {
114
+ let tag: u32 = tag. try_into( ) . map_err( |_| cbor4ii:: core:: error:: DecodeError :: CastOverflow {
115
+ name: & "tag" ,
116
+ } ) ?;
117
+ let val = <#val_ty as cbor4ii:: core:: dec:: Decode <' _>>:: decode( reader) ?;
118
+ #ident:: #name( tag, val)
119
+ } ,
120
+ }
114
121
}
115
- }
116
- _ => panic ! ( "unknown member must be a tag and a value" ) ,
117
- } ,
118
- _ => panic ! ( "named enum unsupported" ) ,
119
- } )
120
- } else {
121
- None
122
- } ;
122
+ _ => panic ! ( "unknown member must be a tag and a value" ) ,
123
+ } ,
124
+ _ => panic ! ( "named enum unsupported" ) ,
125
+ }
126
+ } ) ;
127
+
128
+ if matches ! ( enum_type, EnumType :: Struct ) {
129
+ assert ! ( unknown_arm. is_none( ) , "struct enum does not allow unknown variants" ) ;
130
+ }
123
131
124
132
let fields = iter
125
133
. enumerate ( )
126
134
. map ( |( idx, field) | -> syn:: Arm {
127
135
let idx = idx + 1 ; // skip zero
128
136
let idx: u32 = idx. try_into ( ) . expect ( "enum tags must not exceed 32 bits" ) ;
137
+ let idx = idx as u64 ;
129
138
let name = & field. ident ;
130
139
131
140
assert ! ( field. discriminant. is_none( ) , "custom discriminant is not allowed" ) ;
132
141
assert ! ( !is_unknown( & field. attrs) , "unknown member must be first" ) ;
133
142
134
- match & field. fields {
135
- syn:: Fields :: Unnamed ( fields) => {
136
- if fields. unnamed . len ( ) != 1 {
137
- panic ! ( "enum member only allows one field" ) ;
138
- }
139
-
140
- if * is_unit. get_or_insert ( false ) {
141
- panic ! ( "the number of fields in member must be consistent" ) ;
143
+ match enum_type {
144
+ EnumType :: Unit => {
145
+ assert ! ( is_with( & field. attrs) . is_none( ) , "unit member is not allowed with type" ) ;
146
+ syn:: parse_quote!{
147
+ #idx => #ident:: #name,
142
148
}
143
- let val_ty = & fields. unnamed [ 0 ] . ty ;
149
+ } ,
150
+ EnumType :: One => {
151
+ let val_ty = & field. fields . iter ( ) . next ( ) . unwrap ( ) . ty ;
144
152
let value: syn:: Expr = match is_with ( & field. attrs ) {
145
- Some ( with_type) => syn:: parse_quote!( <#with_type<#val_ty> as cbor4ii:: core:: dec:: Decode <' _>>:: decode( reader) ?. 0 ) ,
153
+ Some ( with_type)
154
+ => syn:: parse_quote!( <#with_type<#val_ty> as cbor4ii:: core:: dec:: Decode <' _>>:: decode( reader) ?. 0 ) ,
146
155
None => syn:: parse_quote!( <#val_ty as cbor4ii:: core:: dec:: Decode <' _>>:: decode( reader) ?)
147
156
} ;
148
157
149
158
syn:: parse_quote!{
150
- #idx => {
151
- let val = #value;
152
- #ident:: #name( val)
153
- } ,
159
+ #idx => #ident:: #name( #value) ,
154
160
}
155
161
} ,
156
- syn:: Fields :: Unit => {
157
- if !* is_unit. get_or_insert ( true ) {
158
- panic ! ( "the number of fields in member must be consistent" ) ;
159
- }
160
- assert ! ( is_with( & field. attrs) . is_none( ) , "unit member is not allowed with type" ) ;
161
-
162
+ EnumType :: Struct => {
163
+ let is_named = field. fields . iter ( ) . all ( |field| field. ident . is_some ( ) ) ;
164
+ let names = field. fields . iter ( )
165
+ . enumerate ( )
166
+ . map ( |( idx, field) | match field. ident . as_ref ( ) {
167
+ Some ( name) => name. clone ( ) ,
168
+ None => {
169
+ let name = format ! ( "unit{idx}" ) ;
170
+ let name = syn:: Ident :: new ( & name, field. span ( ) ) ;
171
+ name
172
+ }
173
+ } )
174
+ . collect :: < Vec < _ > > ( ) ;
175
+ let num = field. fields . len ( ) ;
176
+
177
+ let stmt = field. fields . iter ( )
178
+ . zip ( names. iter ( ) )
179
+ . map ( |( field, field_name) | -> syn:: Stmt {
180
+ let val_ty = & field. ty ;
181
+ match is_with ( & field. attrs ) {
182
+ Some ( with_type) => syn:: parse_quote!{
183
+ let #field_name = <#with_type<#val_ty> as cbor4ii:: core:: dec:: Decode <' _>>:: decode( reader) ?. 0 ;
184
+ } ,
185
+ None => syn:: parse_quote!{
186
+ let #field_name = <#val_ty as cbor4ii:: core:: dec:: Decode <' _>>:: decode( reader) ?;
187
+ }
188
+ }
189
+ } ) ;
190
+ let build_struct: syn:: Expr = is_named
191
+ . then ( || syn:: parse_quote! {
192
+ #ident:: #name { #( #names) , * }
193
+ } )
194
+ . unwrap_or_else ( || syn:: parse_quote! {
195
+ #ident:: #name ( #( #names) , * )
196
+ } ) ;
197
+
162
198
syn:: parse_quote!{
163
- #idx => #ident:: #name,
164
- }
165
- } ,
166
- syn:: Fields :: Named ( _) => panic ! ( "named enum unsupported" )
199
+ #idx => {
200
+ let len = cbor4ii:: core:: types:: Array :: len( reader) ?;
201
+ debug_assert_eq!( len, Some ( #num) ) ;
202
+ #( #stmt) *
203
+ #build_struct
204
+ } ,
205
+ }
206
+ }
167
207
}
168
- } )
169
- . collect :: < Vec < _ > > ( ) ;
208
+ } ) ;
170
209
171
210
let unknown_arm = match unknown_arm {
172
211
Some ( arm) => arm,
@@ -183,26 +222,13 @@ pub fn expand(DeriveInput { ident, data, .. }: DeriveInput) -> syn::ItemImpl {
183
222
}
184
223
} ;
185
224
186
- let head: syn:: Expr = {
187
- let count: usize = match is_unit {
188
- Some ( true ) => 1 ,
189
- Some ( false ) => 2 ,
190
- None => panic ! ( ) ,
191
- } ;
192
- syn:: parse_quote! { {
193
- let n = <cbor4ii:: core:: types:: Array <( ) >>:: len( reader) ?;
194
- debug_assert_eq!( n, Some ( #count) ) ;
195
- } }
196
- } ;
197
-
198
225
syn:: parse_quote! {
199
226
impl <' de> cbor4ii:: core:: dec:: Decode <' de> for #ident {
200
227
#[ inline]
201
228
fn decode<R : cbor4ii:: core:: dec:: Read <' de>>( reader: & mut R )
202
229
-> Result <Self , cbor4ii:: core:: error:: DecodeError <R :: Error >>
203
230
{
204
- #head;
205
- let tag = <u32 as cbor4ii:: core:: dec:: Decode <' _>>:: decode( reader) ?;
231
+ let tag = <cbor4ii:: core:: types:: Tag <( ) >>:: tag( reader) ?;
206
232
let value = match tag {
207
233
#( #fields) *
208
234
#unknown_arm
0 commit comments