1
1
extern crate proc_macro;
2
2
3
+ use macro_magic:: import_tokens_attr;
3
4
use quote:: { quote, ToTokens } ;
4
5
use syn:: {
5
6
braced,
@@ -13,10 +14,14 @@ use syn::{
13
14
Block ,
14
15
Error ,
15
16
Expr ,
17
+ Fields ,
16
18
GenericArgument ,
17
19
Generics ,
18
20
Ident ,
21
+ ImplItem ,
19
22
ImplItemFn ,
23
+ ItemImpl ,
24
+ ItemStruct ,
20
25
Lifetime ,
21
26
Lit ,
22
27
Meta ,
@@ -25,6 +30,7 @@ use syn::{
25
30
PathSegment ,
26
31
Token ,
27
32
Type ,
33
+ Visibility ,
28
34
} ;
29
35
30
36
/// Generates:
@@ -211,6 +217,12 @@ fn parse_name(input: ParseStream, name: &str) -> syn::Result<()> {
211
217
Ok ( ( ) )
212
218
}
213
219
220
+ macro_rules! compile_error {
221
+ ( $span: expr, $( $message: tt) +) => { {
222
+ return Error :: new( $span, format!( $( $message) +) ) . into_compile_error( ) . into( ) ;
223
+ } } ;
224
+ }
225
+
214
226
/// Enables rustdoc links to types that link individually to each type
215
227
/// component.
216
228
#[ proc_macro_attribute]
@@ -242,11 +254,7 @@ pub fn deeplink(
242
254
let rest = & text[ ix + 2 ..] ;
243
255
let end = match rest. find ( ']' ) {
244
256
Some ( v) => v,
245
- None => {
246
- return Error :: new ( attr. span ( ) , "unterminated d[" )
247
- . into_compile_error ( )
248
- . into ( )
249
- }
257
+ None => compile_error ! ( attr. span( ) , "unterminated d[" ) ,
250
258
} ;
251
259
let body = & rest[ ..end] ;
252
260
let post = & rest[ end + 1 ..] ;
@@ -322,20 +330,18 @@ pub fn option_setters(input: proc_macro::TokenStream) -> proc_macro::TokenStream
322
330
setters,
323
331
} = parse_macro_input ! ( input as OptionSettersList ) ;
324
332
325
- let extras = opt_field_name. map ( |name| {
326
- quote ! {
327
- #[ allow( unused) ]
328
- fn options( & mut self ) -> & mut #opt_field_type {
329
- self . #name. get_or_insert_with( <#opt_field_type>:: default )
330
- }
333
+ let extras = quote ! {
334
+ #[ allow( unused) ]
335
+ fn options( & mut self ) -> & mut #opt_field_type {
336
+ self . #opt_field_name. get_or_insert_with( <#opt_field_type>:: default )
337
+ }
331
338
332
- /// Set all options. Note that this will replace all previous values set.
333
- pub fn with_options( mut self , value: impl Into <Option <#opt_field_type>>) -> Self {
334
- self . #name = value. into( ) ;
335
- self
336
- }
339
+ /// Set all options. Note that this will replace all previous values set.
340
+ pub fn with_options( mut self , value: impl Into <Option <#opt_field_type>>) -> Self {
341
+ self . #opt_field_name = value. into( ) ;
342
+ self
337
343
}
338
- } ) ;
344
+ } ;
339
345
340
346
let setters: Vec < _ > = setters
341
347
. into_iter ( )
@@ -350,7 +356,7 @@ pub fn option_setters(input: proc_macro::TokenStream) -> proc_macro::TokenStream
350
356
|| path_eq ( & type_, & [ "bson" , "Bson" ] )
351
357
{
352
358
( quote ! { impl Into <#type_> } , quote ! { value. into( ) } )
353
- } else if let Some ( t) = vec_arg ( & type_) {
359
+ } else if let Some ( t) = inner_type ( & type_, "Vec" ) {
354
360
(
355
361
quote ! { impl IntoIterator <Item = #t> } ,
356
362
quote ! { value. into_iter( ) . collect( ) } ,
@@ -376,12 +382,12 @@ pub fn option_setters(input: proc_macro::TokenStream) -> proc_macro::TokenStream
376
382
. into ( )
377
383
}
378
384
379
- fn vec_arg ( path : & Path ) -> Option < & Type > {
385
+ fn inner_type < ' a > ( path : & ' a Path , outer : & str ) -> Option < & ' a Type > {
380
386
if path. segments . len ( ) != 1 {
381
387
return None ;
382
388
}
383
389
let PathSegment { ident, arguments } = path. segments . first ( ) ?;
384
- if ident != "Vec" {
390
+ if ident != outer {
385
391
return None ;
386
392
}
387
393
let args = if let PathArguments :: AngleBracketed ( angle) = arguments {
@@ -415,20 +421,15 @@ fn path_eq(path: &Path, segments: &[&str]) -> bool {
415
421
}
416
422
417
423
struct OptionSettersList {
418
- opt_field_name : Option < Ident > ,
424
+ opt_field_name : Ident ,
419
425
opt_field_type : Type ,
420
426
setters : Vec < OptionSetter > ,
421
427
}
422
428
423
429
impl Parse for OptionSettersList {
424
430
fn parse ( input : ParseStream ) -> syn:: Result < Self > {
425
- let opt_field_name = if input. peek2 ( Token ! [ : ] ) {
426
- let val = input. parse ( ) ?;
427
- input. parse :: < Token ! [ : ] > ( ) ?;
428
- Some ( val)
429
- } else {
430
- None
431
- } ;
431
+ let opt_field_name = input. parse ( ) ?;
432
+ input. parse :: < Token ! [ : ] > ( ) ?;
432
433
let opt_field_type = input. parse ( ) ?;
433
434
input. parse :: < Token ! [ ; ] > ( ) ?;
434
435
let setters = input
@@ -458,3 +459,141 @@ impl Parse for OptionSetter {
458
459
Ok ( Self { attrs, name, type_ } )
459
460
}
460
461
}
462
+
463
+ #[ import_tokens_attr]
464
+ #[ proc_macro_attribute]
465
+ pub fn option_setters_2 (
466
+ attr : proc_macro:: TokenStream ,
467
+ item : proc_macro:: TokenStream ,
468
+ ) -> proc_macro:: TokenStream {
469
+ let opt_struct = parse_macro_input ! ( attr as ItemStruct ) ;
470
+ let mut impl_in = parse_macro_input ! ( item as ItemImpl ) ;
471
+
472
+ // Gather information about each option struct field
473
+ struct OptInfo {
474
+ name : Ident ,
475
+ attrs : Vec < Attribute > ,
476
+ type_ : Path ,
477
+ }
478
+ let mut opt_info = vec ! [ ] ;
479
+ let fields = match & opt_struct. fields {
480
+ Fields :: Named ( f) => & f. named ,
481
+ _ => compile_error ! ( opt_struct. span( ) , "options struct must have named fields" ) ,
482
+ } ;
483
+ for field in fields {
484
+ if !matches ! ( field. vis, Visibility :: Public ( ..) ) {
485
+ continue ;
486
+ }
487
+ // name
488
+ let name = match & field. ident {
489
+ Some ( f) => f. clone ( ) ,
490
+ None => continue ,
491
+ } ;
492
+ // doc and cfg attrs
493
+ let mut attrs = vec ! [ ] ;
494
+ for attr in & field. attrs {
495
+ if attr. path ( ) . is_ident ( "doc" ) || attr. path ( ) . is_ident ( "cfg" ) {
496
+ attrs. push ( attr. clone ( ) ) ;
497
+ }
498
+ }
499
+ // type, unwrapped from `Option`
500
+ let outer = match & field. ty {
501
+ Type :: Path ( ty) => & ty. path ,
502
+ _ => compile_error ! ( field. span( ) , "invalid type" ) ,
503
+ } ;
504
+ let type_ = match inner_type ( outer, "Option" ) {
505
+ Some ( Type :: Path ( ty) ) => ty. path . clone ( ) ,
506
+ _ => compile_error ! ( field. span( ) , "invalid type" ) ,
507
+ } ;
508
+
509
+ opt_info. push ( OptInfo { name, attrs, type_ } ) ;
510
+ }
511
+
512
+ // Append utility fns to `impl` block item list
513
+ let opt_field_type = & opt_struct. ident ;
514
+ impl_in. items . push ( parse_quote ! {
515
+ #[ allow( unused) ]
516
+ fn options( & mut self ) -> & mut #opt_field_type {
517
+ self . options. get_or_insert_with( <#opt_field_type>:: default )
518
+ }
519
+ } ) ;
520
+ impl_in. items . push ( parse_quote ! {
521
+ /// Set all options. Note that this will replace all previous values set.
522
+ pub fn with_options( mut self , value: impl Into <Option <#opt_field_type>>) -> Self {
523
+ self . options = value. into( ) ;
524
+ self
525
+ }
526
+ } ) ;
527
+ // Append setter fns to `impl` block item list
528
+ for OptInfo { name, attrs, type_ } in opt_info {
529
+ let ( accept, value) = if type_. is_ident ( "String" )
530
+ || type_. is_ident ( "Bson" )
531
+ || path_eq ( & type_, & [ "bson" , "Bson" ] )
532
+ {
533
+ ( quote ! { impl Into <#type_> } , quote ! { value. into( ) } )
534
+ } else if let Some ( t) = inner_type ( & type_, "Vec" ) {
535
+ (
536
+ quote ! { impl IntoIterator <Item = #t> } ,
537
+ quote ! { value. into_iter( ) . collect( ) } ,
538
+ )
539
+ } else {
540
+ ( quote ! { #type_ } , quote ! { value } )
541
+ } ;
542
+ impl_in. items . push ( parse_quote ! {
543
+ #( #attrs) *
544
+ pub fn #name( mut self , value: #accept) -> Self {
545
+ self . options( ) . #name = Some ( #value) ;
546
+ self
547
+ }
548
+ } )
549
+ }
550
+
551
+ // All done.
552
+ impl_in. to_token_stream ( ) . into ( )
553
+ }
554
+
555
+ #[ import_tokens_attr]
556
+ #[ proc_macro_attribute]
557
+ pub fn options_doc (
558
+ attr : proc_macro:: TokenStream ,
559
+ item : proc_macro:: TokenStream ,
560
+ ) -> proc_macro:: TokenStream {
561
+ let setters = parse_macro_input ! ( attr as ItemImpl ) ;
562
+ let mut impl_fn = parse_macro_input ! ( item as ImplItemFn ) ;
563
+
564
+ // Collect a list of names from the setters impl
565
+ let mut setter_names = vec ! [ ] ;
566
+ for item in & setters. items {
567
+ match item {
568
+ ImplItem :: Fn ( item) if matches ! ( item. vis, Visibility :: Public ( ..) ) => {
569
+ setter_names. push ( item. sig . ident . to_token_stream ( ) . to_string ( ) ) ;
570
+ }
571
+ _ => continue ,
572
+ }
573
+ }
574
+
575
+ // Get the rustdoc path to the action type, i.e. the type with generic arguments stripped
576
+ let mut doc_path = match & * setters. self_ty {
577
+ Type :: Path ( p) => p. path . clone ( ) ,
578
+ t => compile_error ! ( t. span( ) , "invalid options doc argument" ) ,
579
+ } ;
580
+ for seg in & mut doc_path. segments {
581
+ seg. arguments = PathArguments :: None ;
582
+ }
583
+ let doc_path = doc_path. to_token_stream ( ) . to_string ( ) ;
584
+
585
+ // Add the list of setters to the rustdoc for the fn
586
+ impl_fn. attrs . push ( parse_quote ! {
587
+ #[ doc = "" ]
588
+ } ) ;
589
+ impl_fn. attrs . push ( parse_quote ! {
590
+ #[ doc = "These methods can be chained before calling `.await` to set options:" ]
591
+ } ) ;
592
+ for name in setter_names {
593
+ let docstr = format ! ( " * [`{0}`]({1}::{0})" , name, doc_path) ;
594
+ impl_fn. attrs . push ( parse_quote ! {
595
+ #[ doc = #docstr]
596
+ } ) ;
597
+ }
598
+ impl_fn. into_token_stream ( ) . into ( )
599
+ }
0 commit comments