1
1
extern crate proc_macro;
2
2
3
- use macro_magic:: import_tokens_attr;
3
+ use macro_magic:: { import_tokens_attr, mm_core :: ForeignPath } ;
4
4
use quote:: { quote, ToTokens } ;
5
5
use syn:: {
6
6
braced,
7
+ bracketed,
7
8
parenthesized,
8
9
parse:: { Parse , ParseStream } ,
9
10
parse_macro_input,
10
11
parse_quote,
11
12
parse_quote_spanned,
13
+ punctuated:: Punctuated ,
12
14
spanned:: Spanned ,
15
+ token:: Bracket ,
13
16
Attribute ,
14
17
Block ,
15
18
Error ,
@@ -206,15 +209,15 @@ impl Parse for ActionImplAttrs {
206
209
}
207
210
208
211
/// Parse an identifier with a specific expected value.
209
- fn parse_name ( input : ParseStream , name : & str ) -> syn:: Result < ( ) > {
212
+ fn parse_name ( input : ParseStream , name : & str ) -> syn:: Result < Ident > {
210
213
let ident = input. parse :: < Ident > ( ) ?;
211
214
if ident. to_string ( ) != name {
212
215
return Err ( Error :: new (
213
216
ident. span ( ) ,
214
217
format ! ( "expected '{}', got '{}'" , name, ident) ,
215
218
) ) ;
216
219
}
217
- Ok ( ( ) )
220
+ Ok ( ident )
218
221
}
219
222
220
223
macro_rules! compile_error {
@@ -461,13 +464,15 @@ impl Parse for OptionSetter {
461
464
}
462
465
463
466
#[ import_tokens_attr]
467
+ #[ with_custom_parsing( OptionSettersArgs ) ]
464
468
#[ proc_macro_attribute]
465
469
pub fn option_setters_2 (
466
470
attr : proc_macro:: TokenStream ,
467
471
item : proc_macro:: TokenStream ,
468
472
) -> proc_macro:: TokenStream {
469
473
let opt_struct = parse_macro_input ! ( attr as ItemStruct ) ;
470
474
let mut impl_in = parse_macro_input ! ( item as ItemImpl ) ;
475
+ let args = parse_macro_input ! ( __custom_tokens as OptionSettersArgs ) ;
471
476
472
477
// Gather information about each option struct field
473
478
struct OptInfo {
@@ -545,21 +550,134 @@ pub fn option_setters_2(
545
550
self . options( ) . #name = Some ( #value) ;
546
551
self
547
552
}
553
+ } ) ;
554
+ }
555
+
556
+ // Build rustdoc information.
557
+ let doc_name = args. doc_name ;
558
+ let mut doc_impl = impl_in. clone ( ) ;
559
+ // Synthesize a fn entry for each extra listed so it'll get a rustdoc entry
560
+ if let Some ( ( _, extra) ) = args. extra {
561
+ for name in & extra. names {
562
+ doc_impl. items . push ( parse_quote ! {
563
+ pub fn #name( & self ) { }
564
+ } ) ;
565
+ }
566
+ }
567
+
568
+ // All done. Export the tokens for doc use as their own distinct (uncompiled) item.
569
+ quote ! {
570
+ #impl_in
571
+
572
+ #[ macro_magic:: export_tokens_no_emit( #doc_name) ]
573
+ #doc_impl
574
+ }
575
+ . into ( )
576
+ }
577
+
578
+ struct OptionSettersArgs {
579
+ source_text : ( Ident , Token ! [ =] ) , // source =
580
+ foreign_path : syn:: Path ,
581
+ name_text : ( Token ! [ , ] , Ident , Token ! [ =] ) , // , doc_name =
582
+ doc_name : Ident ,
583
+ extra : Option < ( Token ! [ , ] , OptionSettersArgsExtra ) > ,
584
+ }
585
+
586
+ #[ derive( Debug ) ]
587
+ struct OptionSettersArgsExtra {
588
+ extra_text : ( Ident , Token ! [ =] ) , // extra =
589
+ bracket : Bracket ,
590
+ names : Punctuated < Ident , Token ! [ , ] > ,
591
+ }
592
+
593
+ impl Parse for OptionSettersArgs {
594
+ fn parse ( input : ParseStream ) -> syn:: Result < Self > {
595
+ let source_text = ( parse_name ( input, "source" ) ?, input. parse ( ) ?) ;
596
+ let foreign_path = input. parse ( ) ?;
597
+ let name_text = (
598
+ input. parse ( ) ?,
599
+ parse_name ( input, "doc_name" ) ?,
600
+ input. parse ( ) ?,
601
+ ) ;
602
+ let doc_name = input. parse ( ) ?;
603
+ let extra = if input. is_empty ( ) {
604
+ None
605
+ } else {
606
+ Some ( ( input. parse ( ) ?, input. parse ( ) ?) )
607
+ } ;
608
+ Ok ( Self {
609
+ source_text,
610
+ foreign_path,
611
+ name_text,
612
+ doc_name,
613
+ extra,
548
614
} )
549
615
}
616
+ }
617
+
618
+ impl ToTokens for OptionSettersArgs {
619
+ fn to_tokens ( & self , tokens : & mut proc_macro2:: TokenStream ) {
620
+ let Self {
621
+ source_text,
622
+ foreign_path,
623
+ name_text,
624
+ doc_name,
625
+ extra,
626
+ } = & self ;
627
+ tokens. extend ( source_text. 0 . to_token_stream ( ) ) ;
628
+ tokens. extend ( source_text. 1 . to_token_stream ( ) ) ;
629
+ tokens. extend ( foreign_path. to_token_stream ( ) ) ;
630
+ tokens. extend ( name_text. 0 . to_token_stream ( ) ) ;
631
+ tokens. extend ( name_text. 1 . to_token_stream ( ) ) ;
632
+ tokens. extend ( name_text. 2 . to_token_stream ( ) ) ;
633
+ tokens. extend ( doc_name. to_token_stream ( ) ) ;
634
+ if let Some ( extra) = extra {
635
+ tokens. extend ( extra. 0 . to_token_stream ( ) ) ;
636
+ tokens. extend ( extra. 1 . to_token_stream ( ) ) ;
637
+ }
638
+ }
639
+ }
550
640
551
- // All done.
552
- impl_in. to_token_stream ( ) . into ( )
641
+ impl ForeignPath for OptionSettersArgs {
642
+ fn foreign_path ( & self ) -> & syn:: Path {
643
+ & self . foreign_path
644
+ }
645
+ }
646
+
647
+ impl Parse for OptionSettersArgsExtra {
648
+ fn parse ( input : ParseStream ) -> syn:: Result < Self > {
649
+ let extra_text = ( parse_name ( input, "extra" ) ?, input. parse :: < Token ! [ =] > ( ) ?) ;
650
+ let content;
651
+ let bracket = bracketed ! ( content in input) ;
652
+ let names = Punctuated :: parse_separated_nonempty ( & content) ?;
653
+ Ok ( Self {
654
+ extra_text,
655
+ bracket,
656
+ names,
657
+ } )
658
+ }
659
+ }
660
+
661
+ impl ToTokens for OptionSettersArgsExtra {
662
+ fn to_tokens ( & self , tokens : & mut proc_macro2:: TokenStream ) {
663
+ tokens. extend ( self . extra_text . 0 . to_token_stream ( ) ) ;
664
+ tokens. extend ( self . extra_text . 1 . to_token_stream ( ) ) ;
665
+ self . bracket . surround ( tokens, |content| {
666
+ content. extend ( self . names . to_token_stream ( ) ) ;
667
+ } ) ;
668
+ }
553
669
}
554
670
555
671
#[ import_tokens_attr]
672
+ #[ with_custom_parsing( OptionsDocArgs ) ]
556
673
#[ proc_macro_attribute]
557
674
pub fn options_doc (
558
675
attr : proc_macro:: TokenStream ,
559
676
item : proc_macro:: TokenStream ,
560
677
) -> proc_macro:: TokenStream {
561
678
let setters = parse_macro_input ! ( attr as ItemImpl ) ;
562
679
let mut impl_fn = parse_macro_input ! ( item as ImplItemFn ) ;
680
+ let args = parse_macro_input ! ( __custom_tokens as OptionsDocArgs ) ;
563
681
564
682
// Collect a list of names from the setters impl
565
683
let mut setter_names = vec ! [ ] ;
@@ -586,8 +704,12 @@ pub fn options_doc(
586
704
impl_fn. attrs . push ( parse_quote ! {
587
705
#[ doc = "" ]
588
706
} ) ;
707
+ let preamble = format ! (
708
+ "These methods can be chained before `{}` to set options:" ,
709
+ if args. is_async( ) { ".await" } else { "run" }
710
+ ) ;
589
711
impl_fn. attrs . push ( parse_quote ! {
590
- #[ doc = "These methods can be chained before calling `.await` to set options:" ]
712
+ #[ doc = #preamble ]
591
713
} ) ;
592
714
for name in setter_names {
593
715
let docstr = format ! ( " * [`{0}`]({1}::{0})" , name, doc_path) ;
@@ -597,3 +719,43 @@ pub fn options_doc(
597
719
}
598
720
impl_fn. into_token_stream ( ) . into ( )
599
721
}
722
+
723
+ struct OptionsDocArgs {
724
+ foreign_path : syn:: Path ,
725
+ sync : Option < ( Token ! [ , ] , Ident ) > ,
726
+ }
727
+
728
+ impl OptionsDocArgs {
729
+ fn is_async ( & self ) -> bool {
730
+ self . sync . is_none ( )
731
+ }
732
+ }
733
+
734
+ impl Parse for OptionsDocArgs {
735
+ fn parse ( input : ParseStream ) -> syn:: Result < Self > {
736
+ let foreign_path = input. parse ( ) ?;
737
+ let sync = if input. is_empty ( ) {
738
+ None
739
+ } else {
740
+ Some ( ( input. parse ( ) ?, parse_name ( input, "sync" ) ?) )
741
+ } ;
742
+
743
+ Ok ( Self { foreign_path, sync } )
744
+ }
745
+ }
746
+
747
+ impl ToTokens for OptionsDocArgs {
748
+ fn to_tokens ( & self , tokens : & mut proc_macro2:: TokenStream ) {
749
+ tokens. extend ( self . foreign_path . to_token_stream ( ) ) ;
750
+ if let Some ( ( comma, ident) ) = & self . sync {
751
+ tokens. extend ( comma. to_token_stream ( ) ) ;
752
+ tokens. extend ( ident. to_token_stream ( ) ) ;
753
+ }
754
+ }
755
+ }
756
+
757
+ impl ForeignPath for OptionsDocArgs {
758
+ fn foreign_path ( & self ) -> & syn:: Path {
759
+ & self . foreign_path
760
+ }
761
+ }
0 commit comments