worktable_macros 0.9.0

Proc-macro companion crate for worktable: the worktable! macro and its derives. Formerly published as worktable_codegen.
Documentation
use crate::common::name_generator::WorktableNameGenerator;
use crate::generators::read_only::ReadOnlyGenerator;
use convert_case::{Case, Casing};
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;

impl ReadOnlyGenerator {
    pub fn gen_row_def(&mut self) -> TokenStream {
        let def = self.gen_row_type();
        let table_row_impl = self.gen_row_table_row_impl();
        let row_fields_enum = self.gen_row_fields_enum();
        let query_impl = self.gen_query_impl();

        quote! {
            #def
            #table_row_impl
            #row_fields_enum
            #query_impl
        }
    }

    fn gen_row_table_row_impl(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string());
        let ident = name_generator.get_row_type_ident();
        let primary_key_ident = name_generator.get_primary_key_type_ident();

        let primary_key = self.pk.clone().expect("should be set in `Generator` at this point");
        let primary_key_columns_clone = if primary_key.values.len() == 1 {
            let pk_field = primary_key
                .values
                .keys()
                .next()
                .expect("should exist as length is checked");
            quote! {
                self.#pk_field.clone().into()
            }
        } else {
            let vals = primary_key
                .values
                .keys()
                .map(|i| {
                    quote! {
                        self.#i.clone()
                    }
                })
                .collect::<Vec<_>>();
            quote! {
                (#(#vals),*).into()
            }
        };

        quote! {
            impl TableRow<#primary_key_ident> for #ident {

                fn get_primary_key(&self) -> #primary_key_ident {
                    #primary_key_columns_clone
                }
            }
        }
    }

    fn gen_row_type(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string());
        let ident = name_generator.get_row_type_ident();

        let mut rows = vec![quote! {}; self.columns.field_positions.len()];
        for (i, pos) in &self.columns.field_positions {
            let type_ = self.columns.columns_map.get(i).unwrap();
            rows[*pos] = quote! {pub #i: #type_,}
        }

        quote! {
            #[derive(rkyv::Archive, Debug, rkyv::Deserialize, Clone, rkyv::Serialize, PartialEq, MemStat)]
            #[rkyv(derive(Debug))]
            #[repr(C)]
            pub struct #ident {
                #(#rows)*
            }
        }
    }

    fn gen_query_impl(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string());
        let ident = name_generator.get_row_type_ident();

        quote! {
            impl Query<#ident> for #ident {
                fn merge(self, row: #ident) -> #ident {
                    self
                }
            }
        }
    }

    fn gen_row_fields_enum(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string());
        let ident = name_generator.get_row_type_ident();

        let enum_name = Ident::new(format!("{ident}Fields").as_str(), Span::mixed_site());

        let rows: Vec<_> = self
            .columns
            .columns_map
            .keys()
            .map(|name| {
                let name_pascal = Ident::new(name.to_string().to_case(Case::Pascal).as_str(), Span::mixed_site());
                quote! { #name_pascal, }
            })
            .collect();

        quote! {
            #[derive(rkyv::Archive, Debug, rkyv::Deserialize, Clone, rkyv::Serialize, PartialEq)]
            #[rkyv(derive(Debug))]
            #[repr(C)]
            pub enum #enum_name {
                #(#rows)*
            }
        }
    }
}