Hello with macro_rules pattern

I am trying to write a macro pattern that will match any standard impl definition so I can pull apart the parts in my macro.

Can anyone point me at an example of a macro that does this? My challenge is that I have trouble understanding what the different macro argument types mean.

Parsing an impl definition is beyond what macro_rules can do. Even just correctly parsing the set of generic parameters like impl<A, B: Trait<A>> is already extremely involved and requires something like tt-call to stay sane.

Consider instead writing your macro as a procedural macro (stabilized in Rust 1.30) and using Syn to parse the input as a syn::ItemImpl which contains the components of the impl definition broken out into a syntax tree for you.

1 Like

Perhaps I should qualify that I don't need to parse the entire definition, I just need to match it. My goal is to be able to match something like

display_as_template!(impl<T: ...> DisplayAs<HTML> for Foo<T> {
  template: "foo.html"
});

where I don't need to do anything with the impl portion except reproduce it and extract HTML and the template filename.

That is still hard in macro_rules and should be written as a procedural macro.

extern crate proc_macro;
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream, Result};
use syn::{braced, Generics, LitStr, Token, Type};

mod kw {
    syn::custom_keyword!(DisplayAs);
    syn::custom_keyword!(template);
}

struct ImplDisplayAs {
    generics: Generics,
    repr: Type,
    self_type: Type,
    template: LitStr,
}

impl Parse for ImplDisplayAs {
    fn parse(input: ParseStream) -> Result<Self> {
        // impl<T: ...>
        input.parse::<Token![impl]>()?;
        let generics: Generics = input.parse()?;

        // DisplayAs<$repr>
        input.parse::<kw::DisplayAs>()?;
        input.parse::<Token![<]>()?;
        let repr: Type = input.parse()?;
        input.parse::<Token![>]>()?;

        // for Foo<T>
        input.parse::<Token![for]>()?;
        let self_type: Type = input.parse()?;

        // { template: "foo.html" }
        let body;
        braced!(body in input);
        body.parse::<kw::template>()?;
        body.parse::<Token![:]>()?;
        let template: LitStr = body.parse()?;

        Ok(ImplDisplayAs { generics, repr, self_type, template })
    }
}

#[proc_macro]
pub fn display_as_template(input: TokenStream) -> TokenStream {
    let ImplDisplayAs {
        generics,
        repr,
        self_type,
        template,
    } = syn::parse_macro_input!(input);

    TokenStream::from(quote! {
        fn main() {
            println!("generics = {}", stringify!(#generics));
            println!("repr = {}", stringify!(#repr));
            println!("self_type = {}", stringify!(#self_type));
            println!("template = {}", stringify!(#template));
        }
    })
}
1 Like