Skip to main content

uncollate_macro/
lib.rs

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}