0

I am writing a procedural macro that takes the fields of a struct and sends them to another method:

pub fn my_helper_macro_builder(macro_data: &MacroTokens) -> TokenStream {
    // Retrieves the fields and tries to add self to take it's value
    let insert_values: Vec<TokenStream> = fields
        .iter()
        .map(|ident| ident.to_string())
        .collect::<Vec<String>>()
        .iter()
        .map(|column| quote! { self.#column })
        .collect();

    quote! {
        #vis async fn insert(&self) {
            <#ty as CrudOperations<#ty>>::__insert(
                #table_name,
                #column_names_pretty,
                &[
                    #(#insert_values),*
                ]
            ).await;
        }
    }
}

When I try to use the self that should be received on the #vis async fn insert(&self) method, I receive the error:

expected one of `,`, `.`, `;`, `?`, `]`, or an operator, found `"id"

If I try to wire the &self. available on the signature of the quoted macro into the quote! {} itself, I receive this compiler error:

   |
8  |   pub fn generate_insert_tokens(macro_data: &MacroTokens) -> TokenStream {
   |          ---------------------- this function can't have a `self` parameter
...
48 | /     quote! {
49 | |         #vis async fn insert(&self) {
50 | |             <#ty as CrudOperations<#ty>>::__insert(
51 | |                 #table_name, 
                       #column_names_pretty, 
                       &[
                            #self.#(#insert_values),*
                        ] 
...  |
57 | |         }
58 | |     }
   | |_____^ `self` value is a keyword only available in methods with a `self` parameter
   |
   = note: this error originates in the macro `$crate::quote_token_with_context` 
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Alex Vergara
  • 1,766
  • 1
  • 10
  • 29
  • You are stringifying your ident, but you _want_ an ident. [Look at what the macro generates](https://stackoverflow.com/q/28580386/155423) and it will likely be clear. I'm guessing you have created code that says `self."id"` instead of `self.id`. Try something like `let insert_values = fields.iter().map(|column| quote! { self.#column })`. (Note the performance gain by avoiding the unneeded `collect`s as well). – Shepmaster Mar 08 '22 at 15:49

1 Answers1

0

As @Shepmaster pointed, the issue it's that I was stringifying the name of the column, so the final code will look like this:

pub fn my_helper_macro_builder(macro_data: &MacroTokens) -> TokenStream {
    // Retrieves the fields and tries to add self to take it's value
    let insert_values = fields.iter().map( |ident| {
        quote! { &self.#ident }
    });

    quote! {
        #vis async fn insert(&self) {
            <#ty as CrudOperations<#ty>>::__insert(
                #table_name,
                #column_names_pretty,
                &[
                    #(#insert_values),*
                ]
            ).await;
        }
    }
}

Alex Vergara
  • 1,766
  • 1
  • 10
  • 29