1
1
use crate :: branch:: Traitified ;
2
2
use proc_macro:: TokenStream ;
3
- use proc_macro2:: Span ;
3
+ use proc_macro2:: { extra , Span } ;
4
4
use quote:: quote;
5
5
use std:: fmt:: Write ;
6
6
use std:: iter:: FilterMap ;
7
7
use syn:: {
8
- punctuated:: Punctuated , AngleBracketedGenericArguments , GenericParam , Generics , Ident , Item ,
9
- ItemEnum , ItemStruct , Meta , Path , PathArguments , TraitBoundModifier , Type , TypeParam ,
10
- TypeParamBound , TypePath ,
8
+ punctuated:: Punctuated , AngleBracketedGenericArguments , Expr , Fields , GenericParam , Generics ,
9
+ Ident , Item , ItemEnum , ItemStruct , Meta , Path , PathArguments , TraitBoundModifier , Type ,
10
+ TypeParam , TypeParamBound , TypePath ,
11
11
} ;
12
12
13
13
pub struct MockPrepared {
@@ -174,44 +174,78 @@ pub fn parse_fields_and_generate_for_values(schtruct: &mut ItemStruct) -> TokenS
174
174
}
175
175
176
176
#[ allow( clippy:: option_if_let_else) ]
177
- pub fn parse_fields_and_generate_variant ( enoom : & ItemEnum ) -> TokenStream {
177
+ pub fn parse_fields_and_generate_variant ( enoom : & mut ItemEnum ) -> TokenStream {
178
178
let enum_name = enoom. ident . clone ( ) ;
179
+ let extracted = Extracted :: with_ident ( enum_name. clone ( ) ) ;
180
+ let mocked = prepare_mock_name ( & extracted) ;
179
181
180
- let tok = if let Some ( variant) = enoom. variants . iter ( ) . find ( |field| {
182
+ let mocked_name = & mocked. name ;
183
+ let tok = if let Some ( variant) = enoom. variants . iter_mut ( ) . find ( |field| {
181
184
field
182
185
. attrs
183
186
. iter ( )
184
- . any ( |attr| attr. meta . path ( ) . is_ident ( "mocked_value" ) )
185
- } ) {
187
+ . any ( |attr| {
188
+ if let Meta :: NameValue ( nv) = & attr. meta && nv. path . is_ident ( "mocked_with" ) {
189
+ true
190
+ } else {
191
+ false
192
+ }
193
+ } )
194
+ } ) {
195
+ let variant_name = & mut variant. ident ;
196
+ if let Meta :: NameValue ( ref mut ml) = variant. attrs . first_mut ( ) . unwrap ( ) . meta {
197
+ let mut value = & mut ml. value ;
198
+ replace_with_mocked ( & mut value, & enum_name, & mocked) ;
199
+ quote ! {
200
+ impl #enum_name {
201
+ pub fn mock_new( ) -> #mocked_name {
202
+ #value
203
+ }
204
+ }
205
+ }
206
+ } else {
207
+ quote ! {
208
+ impl #enum_name {
209
+ pub fn mock_new( ) -> #mocked_name {
210
+ Self :: #variant_name
211
+ }
212
+ }
213
+ }
214
+ }
215
+ }
216
+
217
+ else if let ( Some ( variant) , Some ( field) ) = enoom. variants . iter ( ) . fold ( ( None , None ) , |( picked_value, picked_field) , variant| {
218
+ variant. fields . iter ( ) . fold ( ( picked_value, picked_field) , |( picked_value, picked_field) , field| {
219
+ field. attrs . iter ( ) . fold ( ( picked_value, picked_field) , |( picked_value, field) , attr| {
220
+ if let Meta :: NameValue ( nv) = & attr. meta && nv. path . is_ident ( "mocked_with" ) {
221
+ ( Some ( variant) , Some ( nv. value . clone ( ) ) )
222
+ } else {
223
+ ( None , None )
224
+ }
225
+ } )
226
+ } )
227
+ } )
228
+ {
186
229
let variant_name = & variant. ident ;
187
230
quote ! {
188
231
impl #enum_name {
189
- pub fn mock_new( ) -> Self {
190
- Self :: #variant_name
232
+ pub fn mock_new( ) -> #mocked_name {
233
+ Self :: #variant_name( #field )
191
234
}
192
235
}
193
236
}
194
- } else if let Some ( variant) = enoom. variants . iter ( ) . find ( |field| {
195
- field
196
- . attrs
197
- . iter ( )
198
- . any ( |attr| attr. meta . path ( ) . is_ident ( "mocked_value_with" ) )
199
- } ) {
200
- let variant_name = & variant. ident ;
201
- let value = if let Meta :: List ( ref ml) = variant. attrs . first ( ) . unwrap ( ) . meta {
202
- & ml. tokens
203
- } else {
204
- unreachable ! ( )
205
- } ;
206
-
237
+ } else if let Some ( mocked_value) = get_mocked_value_from_attributes ( enoom, & mocked) {
207
238
quote ! {
208
239
impl #enum_name {
209
- pub fn mock_new( ) -> Self {
210
- Self :: #variant_name ( #value )
240
+ pub fn mock_new( ) -> #mocked_name {
241
+ #mocked_value
211
242
}
212
243
}
213
244
}
214
- } else {
245
+ }
246
+
247
+
248
+ else {
215
249
quote ! {
216
250
impl #enum_name {
217
251
pub fn mock_new( ) -> Self {
@@ -235,6 +269,26 @@ pub fn extract_generics_from_bounds(bounds: &mut Generics) {
235
269
} ) ;
236
270
}
237
271
272
+ pub fn get_mocked_value_from_attributes ( enoom : & mut ItemEnum , mocked : & Extracted ) -> Option < Expr > {
273
+ let name = & enoom. ident ;
274
+ enoom. attrs . iter_mut ( ) . find_map ( |attr| {
275
+ match & mut attr. meta {
276
+ Meta :: NameValue ( ref mut nv) if let Some ( _) = nv. path . get_ident ( ) . map ( |x| * x == "mocked_with" ) => {
277
+ replace_with_mocked ( & mut nv. value , name, mocked) ;
278
+ let path = & nv. value ;
279
+ Some ( syn:: parse ( TokenStream :: from ( quote ! ( <#path>) ) ) . unwrap ( ) )
280
+ } ,
281
+ Meta :: NameValue ( nv) if let Some ( _) = nv. path . get_ident ( ) . map ( |x| * x == "mocked_with_default" ) => {
282
+ Some ( syn:: parse ( TokenStream :: from ( quote ! ( <#name>:: default ( ) ) ) ) . unwrap ( ) )
283
+ } ,
284
+
285
+ _ => {
286
+ None
287
+ } ,
288
+ }
289
+ } )
290
+ }
291
+
238
292
pub fn clean_out_attributes ( item : & mut Item ) {
239
293
match item {
240
294
Item :: Struct ( s) => {
@@ -243,7 +297,7 @@ pub fn clean_out_attributes(item: &mut Item) {
243
297
. attrs
244
298
. extract_if ( |attr| {
245
299
if let Meta :: Path ( ref p) = attr. meta {
246
- p. get_ident ( ) . map ( |x| x . to_string ( ) == "mocked" ) . is_some ( )
300
+ p. get_ident ( ) . map ( |x| * x == "mocked" ) . is_some ( )
247
301
} else {
248
302
false
249
303
}
@@ -252,19 +306,67 @@ pub fn clean_out_attributes(item: &mut Item) {
252
306
} ) ;
253
307
}
254
308
Item :: Enum ( e) => {
309
+ e. attrs . retain ( |attr| {
310
+ if let Meta :: NameValue ( p) = & attr. meta && p. path . get_ident ( ) . map ( |x| * x == "mocked_with" ) . is_some ( ) {
311
+ false
312
+ } else {
313
+ true
314
+ }
315
+ } ) ;
316
+
255
317
e. variants . iter_mut ( ) . for_each ( |field| {
256
318
field. attrs = field
257
319
. attrs
258
- . extract_if ( |attr| {
259
- if let Meta :: Path ( ref p) = attr. meta {
260
- p. get_ident ( ) . map ( |x| x. to_string ( ) == "mocked" ) . is_some ( )
320
+ . iter ( )
321
+ . filter_map ( |attr| {
322
+ if let Meta :: NameValue ( ref p) = attr. meta && p. path . get_ident ( ) . map ( |x| * x == "mocked_with" ) . is_some ( ) {
323
+ None
261
324
} else {
262
- false
325
+ Some ( attr . clone ( ) )
263
326
}
264
- } )
265
- . collect ( ) ;
327
+ } ) . collect ( ) ;
328
+
329
+ field. fields . iter_mut ( ) . for_each ( |field| {
330
+ field. attrs = field. attrs . iter ( ) . filter_map ( |attr| {
331
+ if let Meta :: NameValue ( ref p ) = attr. meta && p. path . get_ident ( ) . map ( |x| * x == "mocked_with" ) . is_some ( ) {
332
+ None
333
+ } else {
334
+ Some ( attr. clone ( ) )
335
+ }
336
+ } ) . collect ( ) ;
337
+
338
+ } ) ;
339
+
266
340
} ) ;
267
341
}
268
342
_ => unreachable ! ( ) ,
269
343
}
270
344
}
345
+
346
+ fn replace_with_mocked ( expr : & mut Expr , name : & Ident , mocked : & Extracted ) {
347
+ match expr {
348
+ Expr :: Path ( ref mut path) => {
349
+ let mocked_segment = path
350
+ . path
351
+ . segments
352
+ . iter_mut ( )
353
+ . find ( |path| path. ident == * name)
354
+ . unwrap ( ) ;
355
+ mocked_segment. ident = mocked. name . clone ( ) ;
356
+ }
357
+
358
+ Expr :: Call ( call) => {
359
+ if let Expr :: Path ( ref mut p) = & mut call. func . as_mut ( ) {
360
+ let mocked_segment = p
361
+ . path
362
+ . segments
363
+ . iter_mut ( )
364
+ . find ( |path| path. ident == * name)
365
+ . unwrap ( ) ;
366
+ mocked_segment. ident = mocked. name . clone ( ) ;
367
+ }
368
+ }
369
+
370
+ _ => todo ! ( "other exprs matter" ) ,
371
+ }
372
+ }
0 commit comments