use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{Data, DeriveInput, Error, parse_macro_input, spanned::Spanned};
#[proc_macro_derive(Uncollate)]
pub fn derive_uncollate(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let data = if let Data::Struct(data) = input.data {
data
} else {
return Error::new(input.span(), "Uncollate can only be derived for structs").into_compile_error().into();
};
let struct_name = &input.ident;
let trait_name = format_ident!("Uncollate{}", struct_name);
let field_names: Vec<_> = data.fields.iter().filter_map(|f| f.ident.as_ref()).collect();
let function_names: Vec<_> = field_names.iter().map(|f| format_ident!("uncoll_{}", f)).collect();
let field_types: Vec<_> = data.fields.iter().map(|f| &f.ty).collect();
let uncollate = quote! {
pub trait #trait_name {
#(fn #function_names(&self) -> std::vec::Vec<#field_types>; )*
}
};
let impls = quote! {
impl<T: std::ops::Deref<Target = [#struct_name]>> #trait_name for T {
#(fn #function_names(&self) -> std::vec::Vec<#field_types> {
self.iter().map(|f| f.#field_names).collect()
})*
}
};
quote! {
#uncollate
#impls
}.into()
}