worktable_macros 0.9.0

Proc-macro companion crate for worktable: the worktable! macro and its derives. Formerly published as worktable_codegen.
Documentation
use convert_case::{Case, Casing};
use proc_macro2::TokenStream;
use quote::quote;

use crate::common::name_generator::WorktableNameGenerator;
use crate::persist_index::generator::Generator;

impl Generator {
    pub fn gen_space_secondary_index_events_type(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_index_ident(&self.struct_def.ident);
        let ident = name_generator.get_space_secondary_index_events_ident();

        let fields: Vec<_> = self
            .field_types
            .iter()
            .map(|(i, t)| {
                quote! {
                    #i: Vec<IndexChangeEvent<
                        IndexPair<#t, Link>
                    >>,
                }
            })
            .collect();

        quote! {
            #[derive(Clone, Debug, Default)]
            pub struct #ident {
                #(#fields)*
            }
        }
    }

    pub fn gen_space_secondary_index_events_impl(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_index_ident(&self.struct_def.ident);
        let ident = name_generator.get_space_secondary_index_events_ident();
        let avt_index_ident = name_generator.get_available_indexes_ident();

        let extend_fn = self.gen_space_secondary_index_events_extend_fn();
        let remove_fn = self.gen_space_secondary_index_events_remove_fn();
        let last_evs_fn = self.gen_space_secondary_index_events_last_evs_fn();
        let first_evs = self.gen_space_secondary_index_events_first_evs_fn();
        let iter_event_ids_fn = self.gen_space_secondary_index_events_iter_event_ids_fn();
        let contains_event_fn = self.gen_space_secondary_index_events_contains_event_fn();
        let split_is_first_fn = self.gen_space_secondary_index_events_is_first_split_fn();
        let sort_fn = self.gen_space_secondary_index_events_sort_fn();
        let validate_fn = self.gen_space_secondary_index_events_validate_fn();
        let is_empty_fn = self.gen_space_secondary_index_events_is_empty_fn();
        let is_unit_fn = self.gen_space_secondary_index_events_is_unit_fn();

        quote! {
            impl TableSecondaryIndexEventsOps<#avt_index_ident> for #ident {
                #extend_fn
                #remove_fn
                #last_evs_fn
                #first_evs
                #iter_event_ids_fn
                #contains_event_fn
                #split_is_first_fn
                #sort_fn
                #validate_fn
                #is_empty_fn
                #is_unit_fn
            }
        }
    }

    fn gen_space_secondary_index_events_sort_fn(&self) -> TokenStream {
        let fields_sort: Vec<_> = self
            .field_types
            .keys()
            .map(|i| {
                quote! {
                    self.#i.sort_by(|ev1, ev2| ev1.id().cmp(&ev2.id()));
                }
            })
            .collect();

        quote! {
            fn sort(&mut self) {
                    #(#fields_sort)*
                }
        }
    }

    fn gen_space_secondary_index_events_first_evs_fn(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_index_ident(&self.struct_def.ident);
        let avt_index_ident = name_generator.get_available_indexes_ident();

        let fields_first: Vec<_> = self
            .field_types
            .keys()
            .map(|i| {
                let camel_case_name = i.to_string().from_case(Case::Snake).to_case(Case::Pascal);
                let index_variant: TokenStream = camel_case_name.parse().unwrap();
                quote! {
                    let first = self.#i.first().map(|ev| ev.id());
                    map.insert(#avt_index_ident::#index_variant, first);
                }
            })
            .collect();

        quote! {
            fn first_evs(&self) -> std::collections::HashMap<#avt_index_ident, Option<IndexChangeEventId>> {
                    let mut map = std::collections::HashMap::new();
                    #(#fields_first)*
                    map
                }
        }
    }

    fn gen_space_secondary_index_events_last_evs_fn(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_index_ident(&self.struct_def.ident);
        let avt_index_ident = name_generator.get_available_indexes_ident();

        let fields_last: Vec<_> = self
            .field_types
            .keys()
            .map(|i| {
                let camel_case_name = i.to_string().from_case(Case::Snake).to_case(Case::Pascal);
                let index_variant: TokenStream = camel_case_name.parse().unwrap();
                quote! {
                    let last = self.#i.last().map(|ev| ev.id());
                    map.insert(#avt_index_ident::#index_variant, last);
                }
            })
            .collect();

        quote! {
            fn last_evs(&self) -> std::collections::HashMap<#avt_index_ident, Option<IndexChangeEventId>> {
                    let mut map = std::collections::HashMap::new();
                    #(#fields_last)*
                    map
                }
        }
    }

    fn gen_space_secondary_index_events_extend_fn(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_index_ident(&self.struct_def.ident);
        let ident = name_generator.get_space_secondary_index_events_ident();

        let fields_extend: Vec<_> = self
            .field_types
            .keys()
            .map(|i| {
                quote! {
                    self.#i.extend(another.#i);
                }
            })
            .collect();

        quote! {
            fn extend(&mut self, another: #ident) {
                    #(#fields_extend)*
                }
        }
    }

    fn gen_space_secondary_index_events_remove_fn(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_index_ident(&self.struct_def.ident);
        let ident = name_generator.get_space_secondary_index_events_ident();

        let fields_remove: Vec<_> = self
            .field_types
            .keys()
            .map(|i| {
                quote! {
                    for ev in &another.#i {
                        if let Ok(pos) = self.#i
                            .binary_search_by(|inner_ev| inner_ev.id().cmp(&ev.id())) {
                            self.#i.remove(pos);
                        }

                    }
                }
            })
            .collect();

        quote! {
            fn remove(&mut self, another: &#ident) {
                    #(#fields_remove)*
                }
        }
    }

    fn gen_space_secondary_index_events_iter_event_ids_fn(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_index_ident(&self.struct_def.ident);
        let avt_index_ident = name_generator.get_available_indexes_ident();

        let fields_iter: Vec<_> = self
            .field_types
            .keys()
            .map(|i| {
                let camel_case_name = i.to_string().from_case(Case::Snake).to_case(Case::Pascal);
                let index_variant: TokenStream = camel_case_name.parse().unwrap();
                quote! {
                    self.#i.iter().map(|ev| (#avt_index_ident::#index_variant, ev.id())).collect::<Vec<_>>()
                }
            })
            .collect();

        quote! {
            fn iter_event_ids(&self) -> impl Iterator<Item = (#avt_index_ident, IndexChangeEventId)> {
                <std::vec::IntoIter<Vec<(#avt_index_ident, IndexChangeEventId)>> as Iterator>::flatten(
                    vec![
                    #(#fields_iter),*
                ]
                    .into_iter()
                    )
                }
        }
    }

    fn gen_space_secondary_index_events_is_first_split_fn(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_index_ident(&self.struct_def.ident);
        let avt_index_ident = name_generator.get_available_indexes_ident();

        let fields_matches: Vec<_> = self
            .field_types
            .keys()
            .map(|i| {
                let camel_case_name = i.to_string().from_case(Case::Snake).to_case(Case::Pascal);
                let index_variant: TokenStream = camel_case_name.parse().unwrap();
                quote! {
                    #avt_index_ident::#index_variant => {
                        if let Some(first) = self.#i.first() {
                            match first {
                                IndexChangeEvent::SplitNode {..} => {
                                    true
                                }
                                _ => {
                                    false
                                }
                            }
                        } else {
                            false
                        }
                    }
                }
            })
            .collect();

        if fields_matches.is_empty() {
            quote! {
                fn is_first_ev_is_split(&self, _index: #avt_index_ident) -> bool {
                    false
                }
            }
        } else {
            quote! {
                fn is_first_ev_is_split(&self, index: #avt_index_ident) -> bool {
                    match index {
                        #(#fields_matches),*
                    }
                }
            }
        }
    }

    fn gen_space_secondary_index_events_contains_event_fn(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_index_ident(&self.struct_def.ident);
        let avt_index_ident = name_generator.get_available_indexes_ident();

        let fields_matches: Vec<_> = self
            .field_types
            .keys()
            .map(|i| {
                let camel_case_name = i.to_string().from_case(Case::Snake).to_case(Case::Pascal);
                let index_variant: TokenStream = camel_case_name.parse().unwrap();
                quote! {
                    #avt_index_ident::#index_variant => {
                        self.#i.iter().map(|ev| ev.id()).any(|ev_id| ev_id == id)
                    }
                }
            })
            .collect();

        if fields_matches.is_empty() {
            quote! {
                fn contains_event(&self, index: #avt_index_ident, id: IndexChangeEventId) -> bool {
                    false
                }
            }
        } else {
            quote! {
                fn contains_event(&self, index: #avt_index_ident, id: IndexChangeEventId) -> bool {
                    match index {
                        #(#fields_matches),*
                    }
                }
            }
        }
    }

    fn gen_space_secondary_index_events_validate_fn(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_index_ident(&self.struct_def.ident);
        let ident = name_generator.get_space_secondary_index_events_ident();

        let fields_validate: Vec<_> = self
            .field_types
            .keys()
            .map(|i| {
                quote! {
                    let #i = validate_events(&mut self.#i);
                }
            })
            .collect();
        let fields_init: Vec<_> = self
            .field_types
            .keys()
            .map(|i| {
                quote! {
                    #i,
                }
            })
            .collect();

        quote! {
            fn validate(&mut self) -> #ident {
                    #(#fields_validate)*
                    Self {
                        #(#fields_init)*
                    }
                }
        }
    }

    fn gen_space_secondary_index_events_is_empty_fn(&self) -> TokenStream {
        let is_empty: Vec<_> = self
            .field_types
            .keys()
            .map(|i| {
                quote! {
                    self.#i.is_empty()
                }
            })
            .collect();
        if is_empty.is_empty() {
            quote! {
                    fn is_empty(&self) -> bool {
                        true
                    }
            }
        } else {
            quote! {
                    fn is_empty(&self) -> bool {
                        #(#is_empty) &&*
                    }
            }
        }
    }

    fn gen_space_secondary_index_events_is_unit_fn(&self) -> TokenStream {
        let is_unit = if self.field_types.is_empty() {
            quote! {
                true
            }
        } else {
            quote! {
                false
            }
        };

        quote! {
            fn is_unit() -> bool {
                    #is_unit
                }
        }
    }
}