I want to write a macro to generate a new
function for a struct, so that it runs get_field_<type_name>
for each field.
#[derive(Debug, AutoStruct)]
struct MyStruct {
geometry: Point,
x: i64
}
should output code like this:
impl AutoStruct for MyStruct {
fn generate<L: Layer>(layer: &L) -> Result<Self, Box<dyn Error>> {
Ok(Self {
geometry: match layer.get_field_Point("geometry")?.unwrap(),
x: match layer.get_field_i64("x")?.unwrap()
})
}
}
Here's the macro I'm writing and it fails:
imports
use proc_macro::{TokenStream};
use proc_macro2::{Span};
use quote::quote;
use syn::{Data, DataStruct, Fields, parse, Type, Ident};
#[proc_macro_derive(AutoStruct)]
pub fn autostruct_macro_derive(input: TokenStream) -> TokenStream {
// Construct a representation of Rust code as a syntax tree
// that we can manipulate
let ast:syn::DeriveInput = parse(input).unwrap();
let name = &ast.ident;
println!("buliding: name: {:?}", name);
let (fields, types, method_names) = match &ast.data {
Data::Struct(DataStruct { fields: Fields::Named(f), ..}) => (
f.named.iter().map(|f| &f.ident),
f.named.iter().map(|f| &f.ty),
f.named.iter().map(|f|
match &f.ty {
Type::Verbatim(ts) => format!("get_field_{}", ts),
_ => panic!("type must be concrete")
}
)
),
_ => panic!("only structs with named fields are supported")
};
let methods = method_names.map(|m| Ident::new(&m, Span::call_site()));
let gen = quote! {
#input
impl AutoStruct for #name {
fn generate<F: Layer>(reader: &F) -> Result<Self, Box<dyn Error>> where Self: Sized {
Ok(#name {
#(
#fields: reader.#methods(#fields)?.unwrap(),
)
})
}
}
};
gen.into()
}
Full error message
(this is at quote! invocation) ^ the trait ToTokens
is not implemented for Map<syn::punctuated::Iter<'_, syn::Field>, [closure@...src/lib.rs:17:24: 17:27]>
required by a bound introduced by this call.
I'm lost on what else should I do to produce a token for each Ident
?