named_generics_bundle_proc_macros/
_mod.rs

1//! Crate not intended for direct use.
2//! Use https:://docs.rs/named-generics-bundle instead.
3// Templated by `cargo-generate` using https://github.com/danielhenrymantilla/proc-macro-template
4#![allow(nonstandard_style, unused_imports, unused_braces)]
5
6use ::core::{
7    mem,
8    ops::Not as _,
9};
10use proc_macro::TokenTree;
11use ::proc_macro::{
12    TokenStream,
13};
14use ::proc_macro2::{*,
15    Span,
16    TokenStream as TokenStream2,
17    TokenTree as TT,
18};
19use ::quote::{
20    format_ident,
21    quote,
22    quote_spanned,
23    ToTokens,
24};
25use ::syn::{*,
26    parse::{Parse, Parser, ParseStream},
27    punctuated::Punctuated,
28    Result, // Explicitly shadow it
29    spanned::Spanned,
30};
31
32mod args;
33
34mod validate_module_path;
35
36///
37#[proc_macro_attribute] pub
38fn named_generics_bundle(
39    args: TokenStream,
40    input: TokenStream,
41) -> TokenStream
42{
43    named_generics_bundle_impl(args.into(), input.into())
44    //  .map(|ret| { println!("{}", ret); ret })
45        .unwrap_or_else(|err| {
46            let mut errors =
47                err .into_iter()
48                    .map(|err| Error::new(
49                        err.span(),
50                        format_args!("`#[named_generics_bundle::named_generics_bundle]`: {}", err),
51                    ))
52            ;
53            let mut err = errors.next().unwrap();
54            errors.for_each(|cur| err.combine(cur));
55            err.to_compile_error()
56        })
57        .into()
58}
59
60/// Like `ItemTrait`, but restricted.
61struct RestrictedItemTrait {
62    attrs: Vec<Attribute>,
63    pub_: Visibility,
64    trait_: Token![trait],
65    TraitName: Ident,
66    supertraits: Punctuated<TypeParamBound, Token![+]>,
67    braces: token::Brace,
68    body: Punctuated<TraitItemType, parse::Nothing>,
69}
70
71impl Parse for RestrictedItemTrait {
72    fn parse(input: ParseStream<'_>) -> Result<Self> {
73        let braces;
74        let mut attrs: Vec<Attribute> = Attribute::parse_outer(input)?;
75        Ok(Self {
76            pub_: input.parse()?,
77            trait_: input.parse()?,
78            TraitName: input.parse()?,
79            supertraits: {
80                let semi: Option<Token![:]> = input.parse()?;
81                let mut ret = Punctuated::default();
82                if semi.is_some() {
83                    while input.peek(token::Brace).not() {
84                        ret.push_value(input.parse()?);
85                        if let Some(plus) = input.parse()? {
86                            ret.push_punct(plus);
87                        } else {
88                            break;
89                        }
90                    }
91                }
92                ret
93            },
94            body: {
95                let inner;
96                braces = braced!(inner in input);
97                let input = &inner;
98
99                attrs.extend(Attribute::parse_inner(input)?.into_iter().map(|mut attr| {
100                    attr.style = AttrStyle::Outer;
101                    attr
102                }));
103
104                Punctuated::parse_terminated(input)?
105            },
106            braces,
107            attrs,
108        })
109    }
110}
111
112fn named_generics_bundle_impl(
113    args: TokenStream2,
114    input: TokenStream2,
115) -> Result<TokenStream2>
116{
117    // By default deny any attribute present.
118    let mut args: args::Args = parse2(args)?;
119    let RestrictedItemTrait {
120        attrs,
121        pub_,
122        trait_,
123        ref TraitName,
124        mut supertraits,
125        braces,
126        body,
127    } = parse2(input)?;
128    let krate = &args.krate.as_ref().map_or_else(|| quote_spanned!(Span::mixed_site()=>
129        ::named_generics_bundle
130    ), ToTokens::to_token_stream);
131
132    if supertraits.empty_or_trailing().not() {
133        supertraits.push_punct(<_>::default());
134    }
135
136    let braced_body = &mut quote::quote!();
137    braces.surround(braced_body, |ts| body.to_tokens(ts));
138
139    let EachTypeName @ _ = body.iter().map(|ty| &ty.ident);
140
141    let ඞTraitName @ _ = &format_ident!(
142        "__proper_macro_rules_scopingඞnamed_generics_bundleඞ{TraitName}",
143    );
144    let TraitName_doclink = &format!(" [`{TraitName}`].");
145
146    let TraitName_macro_invocation_nudge = &format!("\
147 <!--
148 ```rust ,ignore
149 {TraitName}![];
150 ```
151 -->\
152    ");
153
154
155    let if_pub = matches!(pub_, Visibility::Public { .. }).then_some(quote!());
156    let if_pub = if_pub.as_slice();
157
158    let validate_module_path = validate_module_path::validate(krate, &args.module_path);
159
160    if let Some(p) = &mut args.module_path {
161        let last_span = p.segments.last().unwrap().span();
162        p.segments.push_punct(token::PathSep {
163            spans: [last_span; 2],
164        });
165    }
166    let mb_module_path =
167        args.module_path
168            .as_ref()
169            .map(|p| p.to_token_stream().into_iter().collect::<Vec<_>>())
170            .map(|mut tts| match tts.first().unwrap() {
171                // Lift `crate` to `$crate` to be used in the `macro_rules!` def below.
172                TT::Ident(krate) if krate == "crate" => {
173                    let mut dollar = Punct::new('$', Spacing::Joint);
174                    dollar.set_span(krate.span());
175                    tts.insert(0, dollar.into());
176                    tts
177                },
178                _ => unreachable!("as per the current `Parse` implementation"),
179            })
180            .unwrap_or_default()
181    ;
182    // let QualifiedTraitName @ _ = args.module_path.as_ref().map_or_else(
183    //     || TraitName.to_token_stream(),
184    //     |p| {
185    //         let span = p.segments.last().unwrap().span();
186    //         let TraitName @ _ = Ident::new(&TraitName.to_string(), span);
187    //         quote_spanned!(span=>
188    //             #p #TraitName
189    //         )
190    //     },
191    // );
192    //
193    // Note: we do not use this trick to validate anymore, since we have `validate_module_path`.
194    let QualifiedTraitName = TraitName;
195
196    Ok(quote_spanned!(Span::mixed_site()=>
197        #validate_module_path
198
199        #(#attrs)*
200        #pub_
201        #trait_ #TraitName <ඞImpliedDeriveBounds = Self>
202        :
203            #supertraits
204
205            #krate::ඞ::ImpliedPredicate<
206                ඞImpliedDeriveBounds,
207                Impls :
208                    #krate::ඞ::Debug +
209                    #krate::ඞ::Copy +
210                    #krate::ඞ::Ord +
211                    #krate::ඞ::Hash +
212                    #krate::ඞ::Default +
213
214                    #krate::ඞ::Send +
215                    #krate::ඞ::Sync +
216                    #krate::ඞ::Unpin +
217                ,
218            > +
219        #braced_body
220
221        // while we could just use `#TraitName` here, this gives us a simple sanity check
222        // that the provided `module_path` (if any), be correct.
223        impl<ඞDyn : ?#krate::ඞ::core::marker::Sized + #QualifiedTraitName<()>>
224            #TraitName
225        for
226            #krate::ඞ::core::marker::PhantomData<fn(#krate::ඞ::ඞ<()>) -> ඞDyn>
227        {
228            #(
229                type #EachTypeName = ඞDyn::#EachTypeName;
230            )*
231        }
232
233        /// Helper macro to produce an on-the-fly `Sized` "bundle of generic parameters" which
234        /// implements
235        #[doc = #TraitName_doclink]
236        ///
237        /// It also implements `Debug + Copy + Ord + Hash + Default`, so as to be
238        /// dumb-stdlib-`#[derive()]`-friendly.
239
240        // Nudge `rust-analyzer` auto-complete to suggest using square brackets for these macros.
241        #[doc = #TraitName_macro_invocation_nudge]
242        #(#if_pub
243            #[macro_export]
244        )*
245        #[doc(hidden)]
246        macro_rules! #ඞTraitName {(
247            $($named_generics:tt)*
248        ) => (
249            ::core::marker::PhantomData::<fn(()) -> dyn #(#mb_module_path)* #TraitName<
250                (),
251                $($named_generics)*
252            >>
253        )}
254        #[doc(inline)]
255        #pub_ use #ඞTraitName as #TraitName;
256    ))
257}