Generating an Implementation
Walk the fields, build the impl block, and handle generics correctly.
let fields = match &input.data {
Data::Struct(DataStruct { fields: Fields::Named(f), .. }) => &f.named,
_ => return syn::Error::new_spanned(&input, "Describe only supports structs with named fields")
.to_compile_error()
.into(),
};
let lines = fields.iter().map(|f| {
let name = f.ident.as_ref().unwrap();
let label = name.to_string();
quote! { out.push_str(&format!("{}: {:?}\n", #label, self.#name)); }
});
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
quote! {
impl #impl_generics Describe for #name #ty_generics #where_clause {
fn describe(&self) -> String {
let mut out = String::new();
#(#lines)*
out
}
}
}.into()
split_for_impl is the thing people forget. Without it your macro breaks on any generic
struct. #(#lines)* is quoteβs repetition syntax β it splices an iterator of token streams.
Refer to items by full path (::std::string::String) so your macro works in a crate that
shadowed the name.