11extern crate proc_macro;
22
3- use macro_magic:: import_tokens_attr;
3+ use macro_magic:: { import_tokens_attr, mm_core :: ForeignPath } ;
44use quote:: { quote, ToTokens } ;
55use syn:: {
66 braced,
7+ bracketed,
78 parenthesized,
89 parse:: { Parse , ParseStream } ,
910 parse_macro_input,
1011 parse_quote,
1112 parse_quote_spanned,
13+ punctuated:: Punctuated ,
1214 spanned:: Spanned ,
15+ token:: Bracket ,
1316 Attribute ,
1417 Block ,
1518 Error ,
@@ -206,15 +209,15 @@ impl Parse for ActionImplAttrs {
206209}
207210
208211/// 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 > {
210213 let ident = input. parse :: < Ident > ( ) ?;
211214 if ident. to_string ( ) != name {
212215 return Err ( Error :: new (
213216 ident. span ( ) ,
214217 format ! ( "expected '{}', got '{}'" , name, ident) ,
215218 ) ) ;
216219 }
217- Ok ( ( ) )
220+ Ok ( ident )
218221}
219222
220223macro_rules! compile_error {
@@ -461,13 +464,15 @@ impl Parse for OptionSetter {
461464}
462465
463466#[ import_tokens_attr]
467+ #[ with_custom_parsing( OptionSettersArgs ) ]
464468#[ proc_macro_attribute]
465469pub fn option_setters_2 (
466470 attr : proc_macro:: TokenStream ,
467471 item : proc_macro:: TokenStream ,
468472) -> proc_macro:: TokenStream {
469473 let opt_struct = parse_macro_input ! ( attr as ItemStruct ) ;
470474 let mut impl_in = parse_macro_input ! ( item as ItemImpl ) ;
475+ let args = parse_macro_input ! ( __custom_tokens as OptionSettersArgs ) ;
471476
472477 // Gather information about each option struct field
473478 struct OptInfo {
@@ -545,21 +550,134 @@ pub fn option_setters_2(
545550 self . options( ) . #name = Some ( #value) ;
546551 self
547552 }
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,
548614 } )
549615 }
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+ }
550640
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+ }
553669}
554670
555671#[ import_tokens_attr]
672+ #[ with_custom_parsing( OptionsDocArgs ) ]
556673#[ proc_macro_attribute]
557674pub fn options_doc (
558675 attr : proc_macro:: TokenStream ,
559676 item : proc_macro:: TokenStream ,
560677) -> proc_macro:: TokenStream {
561678 let setters = parse_macro_input ! ( attr as ItemImpl ) ;
562679 let mut impl_fn = parse_macro_input ! ( item as ImplItemFn ) ;
680+ let args = parse_macro_input ! ( __custom_tokens as OptionsDocArgs ) ;
563681
564682 // Collect a list of names from the setters impl
565683 let mut setter_names = vec ! [ ] ;
@@ -586,8 +704,12 @@ pub fn options_doc(
586704 impl_fn. attrs . push ( parse_quote ! {
587705 #[ doc = "" ]
588706 } ) ;
707+ let preamble = format ! (
708+ "These methods can be chained before `{}` to set options:" ,
709+ if args. is_async( ) { ".await" } else { "run" }
710+ ) ;
589711 impl_fn. attrs . push ( parse_quote ! {
590- #[ doc = "These methods can be chained before calling `.await` to set options:" ]
712+ #[ doc = #preamble ]
591713 } ) ;
592714 for name in setter_names {
593715 let docstr = format ! ( " * [`{0}`]({1}::{0})" , name, doc_path) ;
@@ -597,3 +719,43 @@ pub fn options_doc(
597719 }
598720 impl_fn. into_token_stream ( ) . into ( )
599721}
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