Hello. I would like to be able to generate a function by consuming a struct an using the struct name, and the name of its fields.
#[derive(Debug)]
struct Output {
data: Vec<usize>,
}
trait MyTrait {
fn do_something(&self) -> Output
where Self: Sized;
}
#[derive(Debug)]
struct MyStruct {
pub foo: usize,
pub bar: usize,
}
I would like to automatically generate an impl of MyTrait
for MyStruct
, like so:
impl MyTrait for MyStruct {
fn do_something(&self) -> Output {
Output {
data: vec![ // FIXME: automatically generate this part
make_number("foo", self.foo),
make_number("bar", self.bar),
],
}
}
}
To do so, I need to create a custom derive macro (in a sub-crate), and add #[derive(MyTrait)
on MyStruct
.
#[proc_macro_derive(MyTrait)]
pub fn my_trait_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
impl_my_trait(&ast)
}
fn impl_my_trait(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let data: syn::Data = ast.data;
// A failed experiment to access to the fields of MyStruct
// The next line doesn't compiles
if let syn::DataStruct(data) = data {
let fields = &data.fields;
// The following part compiles fine if I hardcode the content of data,
// and I correctly get an impl of MyTrait for MyStruct
let gen = quote! {
impl MyTrait for #name { // I can access name, yeah!
fn do_something(&self) -> Output {
Output {
// Pseudo code of what I'm expecting to write
// I may need to create the Vec outside of the `quote!` block, idk
data: fields.iter().map(|&field| {
make_number(stringify!(#field), self.#field)
}).collect(),
}
}
}
};
gen.into()
}
else {
compile_error!("The MyTrait derive macro can only be applied to flat structs.");
}
}
As you can see, I'm nearly there, but I don't understand how I am supposed to access to the fields of a struct using syn.
Some documentation:
Any help would be appreciated. I don't know if I'm struggling because I don't understand the documentation of syn, or if it's because I have a hard time with the syntax of if let Variant(value) = SomeEnum
.
EDIT: Remove RuleSystem
and use MyTrait
instead (I forgot to do it when extracting the code I had from my project.