@@ -115,10 +115,17 @@ pub(super) fn make_cache_key_type(
115
115
116
116
( quote ! { } , quote ! { #key_convert_block} )
117
117
}
118
- ( None , None , _) => (
119
- quote ! { ( #( #input_tys) , * ) } ,
120
- quote ! { ( #( #input_names. clone( ) ) , * ) } ,
121
- ) ,
118
+ ( None , None , _) => {
119
+ let key_tys = input_tys
120
+ . into_iter ( )
121
+ . map ( convert_option_of_ref_to_option_of_owned_type)
122
+ . map ( convert_ref_to_owned_type)
123
+ . collect :: < Vec < Type > > ( ) ;
124
+ (
125
+ quote ! { ( #( #key_tys) , * ) } ,
126
+ quote ! { ( #( #input_names. to_fully_owned( ) ) , * ) } ,
127
+ )
128
+ }
122
129
( Some ( _) , None , _) => panic ! ( "key requires convert to be set" ) ,
123
130
( None , Some ( _) , None ) => panic ! ( "convert requires key or type to be set" ) ,
124
131
}
@@ -218,3 +225,214 @@ pub(super) fn check_with_cache_flag(with_cached_flag: bool, output_string: Strin
218
225
&& !output_string. contains ( "Return" )
219
226
&& !output_string. contains ( "cached::Return" )
220
227
}
228
+
229
+ use ref_inputs:: * ;
230
+ mod ref_inputs {
231
+ use super :: * ;
232
+
233
+ pub ( super ) fn is_option ( ty : & Type ) -> bool {
234
+ if let Type :: Path ( typepath) = ty {
235
+ let segments = & typepath. path . segments ;
236
+ if segments. len ( ) == 1 {
237
+ let segment = segments. first ( ) . unwrap ( ) ;
238
+ if segment. ident == "Option" {
239
+ return true ;
240
+ }
241
+ } else if segments. len ( ) == 3 {
242
+ let segment_idents = segments
243
+ . iter ( )
244
+ . map ( |s| s. ident . to_string ( ) )
245
+ . collect :: < Vec < _ > > ( ) ;
246
+ if segment_idents == [ "std" , "option" , "Option" ] {
247
+ return true ;
248
+ }
249
+ }
250
+ }
251
+ false
252
+ }
253
+
254
+ fn option_generic_arg_unchecked ( ty : & Type ) -> Type {
255
+ if let Type :: Path ( typepath) = ty {
256
+ let segment = & typepath
257
+ . path
258
+ . segments
259
+ . last ( )
260
+ . expect ( "option_generic_arg_unchecked: empty path" ) ;
261
+ if let PathArguments :: AngleBracketed ( brackets) = & segment. arguments {
262
+ if let Some ( syn:: GenericArgument :: Type ( inner_ty) ) = brackets. args . first ( ) {
263
+ return inner_ty. clone ( ) ;
264
+ }
265
+ }
266
+ }
267
+ panic ! ( "option_generic_arg_unchecked: could not extract inner type" ) ;
268
+ }
269
+
270
+ pub ( super ) fn is_option_of_ref ( ty : & Type ) -> bool {
271
+ if is_option ( ty) {
272
+ let inner_ty = option_generic_arg_unchecked ( ty) ;
273
+ if let Type :: Reference ( _) = inner_ty {
274
+ return true ;
275
+ }
276
+ }
277
+
278
+ false
279
+ }
280
+
281
+ pub ( super ) fn convert_ref_to_owned_type ( ty : Type ) -> Type {
282
+ match ty {
283
+ Type :: Reference ( reftype) => * reftype. elem ,
284
+ _ => ty,
285
+ }
286
+ }
287
+
288
+ pub ( super ) fn convert_option_of_ref_to_option_of_owned_type ( ty : Type ) -> Type {
289
+ if is_option_of_ref ( & ty) {
290
+ let inner_ty = option_generic_arg_unchecked ( & ty) ;
291
+ if let Type :: Reference ( reftype) = inner_ty {
292
+ let elem = * reftype. elem ;
293
+ return parse_quote ! { Option < #elem > } ;
294
+ }
295
+ }
296
+ ty
297
+ }
298
+ }
299
+
300
+ #[ cfg( test) ]
301
+ mod test {
302
+ use super :: * ;
303
+ use googletest:: { assert_that, matchers:: eq} ;
304
+ use syn:: parse_quote;
305
+
306
+ macro_rules! type_test {
307
+ ( $test_name: ident, $target_fn: ident syn_ref, $input_type: ty, $expected: expr) => {
308
+ #[ googletest:: test]
309
+ fn $test_name( ) {
310
+ let ty = & parse_quote! { $input_type } ;
311
+ assert_that!( $target_fn( ty) , eq( $expected) ) ;
312
+ }
313
+ } ;
314
+ ( $test_name: ident, $target_fn: ident syn_owned, $input_type: ty, $expected: expr) => {
315
+ #[ googletest:: test]
316
+ fn $test_name( ) {
317
+ let ty = parse_quote! { $input_type } ;
318
+ assert_that!( $target_fn( ty) , eq( $expected) ) ;
319
+ }
320
+ } ;
321
+ }
322
+
323
+ mod convert_ref_to_owned_type {
324
+ use super :: * ;
325
+
326
+ type_test ! {
327
+ returns_the_owned_type_when_given_a_ref_type,
328
+ convert_ref_to_owned_type syn_owned,
329
+ & T ,
330
+ parse_quote!{ T }
331
+ }
332
+
333
+ type_test ! {
334
+ returns_the_same_type_when_given_a_non_ref_type,
335
+ convert_ref_to_owned_type syn_owned,
336
+ T ,
337
+ parse_quote!{ T }
338
+ }
339
+ }
340
+
341
+ mod convert_option_of_ref_to_option_of_owned_type {
342
+ use super :: * ;
343
+
344
+ type_test ! {
345
+ returns_the_owned_option_type_when_given_option_of_ref,
346
+ convert_option_of_ref_to_option_of_owned_type syn_owned,
347
+ Option <& T >,
348
+ parse_quote!{ Option <T > }
349
+ }
350
+
351
+ type_test ! {
352
+ returns_the_same_type_when_given_a_non_option_type,
353
+ convert_option_of_ref_to_option_of_owned_type syn_owned,
354
+ T ,
355
+ parse_quote!{ T }
356
+ }
357
+
358
+ type_test ! {
359
+ returns_the_same_type_when_given_an_option_of_non_ref_type,
360
+ convert_option_of_ref_to_option_of_owned_type syn_owned,
361
+ Option <T >,
362
+ parse_quote!{ Option <T > }
363
+ }
364
+ }
365
+
366
+ mod is_option {
367
+
368
+ mod when_arg_is_ref {
369
+ use super :: super :: * ;
370
+ type_test ! ( returns_true_for_option, is_option syn_ref, Option <& T >, true ) ;
371
+ type_test ! (
372
+ returns_true_for_option_with_fully_qualified_core_path,
373
+ is_option syn_ref,
374
+ std:: option:: Option <& T >,
375
+ true
376
+ ) ;
377
+ type_test ! (
378
+ returns_false_for_custom_type_named_option,
379
+ is_option syn_ref,
380
+ my_module:: Option <& T >,
381
+ false
382
+ ) ;
383
+ }
384
+
385
+ mod when_arg_is_not_ref {
386
+ use super :: super :: * ;
387
+ type_test ! ( returns_true_for_option, is_option syn_ref, Option <T >, true ) ;
388
+ type_test ! (
389
+ returns_true_for_option_with_fully_qualified_core_path,
390
+ is_option syn_ref,
391
+ std:: option:: Option <T >,
392
+ true
393
+ ) ;
394
+ type_test ! (
395
+ returns_false_for_custom_type_named_option,
396
+ is_option syn_ref,
397
+ my_module:: Option <T >,
398
+ false
399
+ ) ;
400
+ type_test ! ( returns_false_for_simple_type, is_option syn_ref, T , false ) ;
401
+ type_test ! ( returns_false_for_a_generic_type, is_option syn_ref, Vec <T >, false ) ;
402
+ }
403
+ }
404
+
405
+ mod is_option_of_ref {
406
+ use super :: * ;
407
+ type_test ! (
408
+ returns_true_for_option_of_ref,
409
+ is_option_of_ref syn_ref,
410
+ Option <& T >,
411
+ true
412
+ ) ;
413
+ type_test ! (
414
+ returns_true_for_option_of_ref_with_fully_qualified_core_path,
415
+ is_option_of_ref syn_ref,
416
+ std:: option:: Option <& T >,
417
+ true
418
+ ) ;
419
+ type_test ! (
420
+ returns_false_for_custom_type_named_option_with_ref_generic_arg,
421
+ is_option_of_ref syn_ref,
422
+ my_module:: Option <& T >,
423
+ false
424
+ ) ;
425
+ type_test ! (
426
+ returns_false_for_option_of_non_ref,
427
+ is_option_of_ref syn_ref,
428
+ Option <T >,
429
+ false
430
+ ) ;
431
+ type_test ! (
432
+ returns_false_for_option_of_non_ref_with_fully_qualified_core_path,
433
+ is_option_of_ref syn_ref,
434
+ std:: option:: Option <T >,
435
+ false
436
+ ) ;
437
+ }
438
+ }
0 commit comments