1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{Data, DeriveInput, Error, parse_macro_input, spanned::Spanned};
4
5#[proc_macro_derive(Uncollate)]
6pub fn derive_uncollate(input: TokenStream) -> TokenStream {
7 let input = parse_macro_input!(input as DeriveInput);
8
9 let data = if let Data::Struct(data) = input.data {
10 data
11 } else {
12 return Error::new(input.span(), "Uncollate can only be derived for structs")
13 .into_compile_error()
14 .into();
15 };
16 let struct_name = &input.ident;
17 let uncollated_name = format_ident!("Uncollated{}", struct_name);
18 let field_names: Vec<_> = data
19 .fields
20 .iter()
21 .filter_map(|f| f.ident.as_ref())
22 .collect();
23 let get_muts: Vec<_> = field_names
24 .iter()
25 .map(|f| format_ident!("{}_mut", f))
26 .collect();
27 let field_types: Vec<_> = data.fields.iter().map(|f| &f.ty).collect();
28 let uncollated = quote! {
29 #[derive(Default)]
30 pub struct #uncollated_name {
31 #(#field_names: std::vec::Vec<#field_types>),*
32 }
33
34 impl #uncollated_name {
35 #(pub fn #field_names(&self) -> &Vec<#field_types>{
36 &self.#field_names
37 })*
38 #(pub fn #get_muts(&mut self) -> &mut Vec<#field_types>{
39 &mut self.#field_names
40 })*
41 }
42
43 impl uncollate::Uncollated for #uncollated_name{}
44
45 impl uncollate::Collated<#uncollated_name> for #struct_name {
46 fn uncollate_one(self, uncollated: &mut #uncollated_name) {
47 #(uncollated.#field_names.push(self.#field_names);)*
48 }
49 }
50 };
51 quote! {
52 #uncollated
53 }
54 .into()
55}