11extern crate proc_macro;
22
3+ use macro_magic:: import_tokens_attr;
34use quote:: { quote, ToTokens } ;
45use syn:: {
56 braced,
@@ -13,10 +14,14 @@ use syn::{
1314 Block ,
1415 Error ,
1516 Expr ,
17+ Fields ,
1618 GenericArgument ,
1719 Generics ,
1820 Ident ,
21+ ImplItem ,
1922 ImplItemFn ,
23+ ItemImpl ,
24+ ItemStruct ,
2025 Lifetime ,
2126 Lit ,
2227 Meta ,
@@ -25,6 +30,7 @@ use syn::{
2530 PathSegment ,
2631 Token ,
2732 Type ,
33+ Visibility ,
2834} ;
2935
3036/// Generates:
@@ -211,6 +217,12 @@ fn parse_name(input: ParseStream, name: &str) -> syn::Result<()> {
211217 Ok ( ( ) )
212218}
213219
220+ macro_rules! compile_error {
221+ ( $span: expr, $( $message: tt) +) => { {
222+ return Error :: new( $span, format!( $( $message) +) ) . into_compile_error( ) . into( ) ;
223+ } } ;
224+ }
225+
214226/// Enables rustdoc links to types that link individually to each type
215227/// component.
216228#[ proc_macro_attribute]
@@ -242,11 +254,7 @@ pub fn deeplink(
242254 let rest = & text[ ix + 2 ..] ;
243255 let end = match rest. find ( ']' ) {
244256 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[" ) ,
250258 } ;
251259 let body = & rest[ ..end] ;
252260 let post = & rest[ end + 1 ..] ;
@@ -322,20 +330,18 @@ pub fn option_setters(input: proc_macro::TokenStream) -> proc_macro::TokenStream
322330 setters,
323331 } = parse_macro_input ! ( input as OptionSettersList ) ;
324332
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+ }
331338
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
337343 }
338- } ) ;
344+ } ;
339345
340346 let setters: Vec < _ > = setters
341347 . into_iter ( )
@@ -350,7 +356,7 @@ pub fn option_setters(input: proc_macro::TokenStream) -> proc_macro::TokenStream
350356 || path_eq ( & type_, & [ "bson" , "Bson" ] )
351357 {
352358 ( 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" ) {
354360 (
355361 quote ! { impl IntoIterator <Item = #t> } ,
356362 quote ! { value. into_iter( ) . collect( ) } ,
@@ -376,12 +382,12 @@ pub fn option_setters(input: proc_macro::TokenStream) -> proc_macro::TokenStream
376382 . into ( )
377383}
378384
379- fn vec_arg ( path : & Path ) -> Option < & Type > {
385+ fn inner_type < ' a > ( path : & ' a Path , outer : & str ) -> Option < & ' a Type > {
380386 if path. segments . len ( ) != 1 {
381387 return None ;
382388 }
383389 let PathSegment { ident, arguments } = path. segments . first ( ) ?;
384- if ident != "Vec" {
390+ if ident != outer {
385391 return None ;
386392 }
387393 let args = if let PathArguments :: AngleBracketed ( angle) = arguments {
@@ -415,20 +421,15 @@ fn path_eq(path: &Path, segments: &[&str]) -> bool {
415421}
416422
417423struct OptionSettersList {
418- opt_field_name : Option < Ident > ,
424+ opt_field_name : Ident ,
419425 opt_field_type : Type ,
420426 setters : Vec < OptionSetter > ,
421427}
422428
423429impl Parse for OptionSettersList {
424430 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 ! [ : ] > ( ) ?;
432433 let opt_field_type = input. parse ( ) ?;
433434 input. parse :: < Token ! [ ; ] > ( ) ?;
434435 let setters = input
@@ -458,3 +459,141 @@ impl Parse for OptionSetter {
458459 Ok ( Self { attrs, name, type_ } )
459460 }
460461}
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