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 proc_macro2::{Ident, Span, TokenStream};
use quote::quote;

impl ReadOnlyGenerator {
    pub fn gen_locks_def(&self) -> TokenStream {
        let type_ = self.gen_locks_type();
        let impl_ = self.gen_locks_impl();

        quote! {
            #type_
            #impl_
        }
    }

    fn gen_locks_type(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string());
        let lock_ident = name_generator.get_lock_type_ident();

        let rows: Vec<_> = self
            .columns
            .columns_map
            .keys()
            .map(|i| {
                let name = Ident::new(format!("{i}_lock").as_str(), Span::mixed_site());
                quote! { #name: Option<std::sync::Arc<Lock>>, }
            })
            .collect();

        quote! {
             #[derive(Debug, Clone)]
             pub struct #lock_ident {
                #(#rows)*
            }
        }
    }

    fn gen_locks_impl(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string());
        let lock_ident = name_generator.get_lock_type_ident();

        let new_fn = self.gen_new_fn();
        let row_impl = self.gen_lock_row_impl();

        quote! {
            impl #lock_ident {
                #new_fn
            }

            #row_impl
        }
    }

    fn gen_lock_row_impl(&self) -> TokenStream {
        let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string());
        let lock_ident = name_generator.get_lock_type_ident();

        let is_locked_fn = self.gen_is_locked_fn();
        let with_lock_fn = self.gen_with_lock_fn();
        let lock_fn = self.gen_lock_fn();
        let merge_fn = self.gen_merge_fn();

        quote! {
            impl RowLock for #lock_ident {
                fn new() -> Self {
                    #lock_ident::new()
                }
                #is_locked_fn
                #lock_fn
                #with_lock_fn
                #merge_fn
            }
        }
    }

    fn gen_is_locked_fn(&self) -> TokenStream {
        let rows: Vec<_> = self
            .columns
            .columns_map
            .keys()
            .map(|i| {
                let col = Ident::new(format!("{i}_lock").as_str(), Span::mixed_site());
                quote! { self.#col.as_ref().map(|l| l.is_locked()).unwrap_or(false)  }
            })
            .collect();

        quote! {
            fn is_locked(&self) -> bool {
                #(#rows) ||*
            }
        }
    }

    fn gen_new_fn(&self) -> TokenStream {
        let rows: Vec<_> = self
            .columns
            .columns_map
            .keys()
            .map(|i| {
                let col = Ident::new(format!("{i}_lock").as_str(), Span::mixed_site());
                quote! { #col: None }
            })
            .collect();

        quote! {
            pub fn new() -> Self {
                Self {
                    #(#rows),*
                }
            }
        }
    }

    fn gen_with_lock_fn(&self) -> TokenStream {
        let rows: Vec<_> = self
            .columns
            .columns_map
            .keys()
            .map(|i| {
                let col = Ident::new(format!("{i}_lock").as_str(), Span::mixed_site());
                quote! { #col: Some(lock.clone()) }
            })
            .collect();

        quote! {
             fn with_lock(id: u16) -> (Self, std::sync::Arc<Lock>) {
                let lock = std::sync::Arc::new(Lock::new(id));
                (
                    Self {
                        #(#rows),*
                    },
                    lock
                )
            }
        }
    }

    fn gen_lock_fn(&self) -> TokenStream {
        let rows: Vec<_> = self
            .columns
            .columns_map
            .keys()
            .map(|i| {
                let col = Ident::new(format!("{i}_lock").as_str(), Span::mixed_site());
                quote! {
                    if let Some(lock) = &self.#col {
                        set.insert(lock.clone());
                    }
                    self.#col = Some(lock.clone());
                }
            })
            .collect();

        quote! {
            #[allow(clippy::mutable_key_type)]
             fn lock(&mut self, id: u16) -> (std::collections::HashSet<std::sync::Arc<Lock>>,  std::sync::Arc<Lock>) {
                let mut set = std::collections::HashSet::new();
                let lock = std::sync::Arc::new(Lock::new(id));
                #(#rows)*

                (set, lock)
            }
        }
    }

    fn gen_merge_fn(&self) -> TokenStream {
        let rows: Vec<_> = self
            .columns
            .columns_map
            .keys()
            .map(|col| {
                let col = Ident::new(format!("{col}_lock").as_str(), Span::mixed_site());
                quote! {
                    if let Some(#col) = &other.#col {
                        if self.#col.is_none() {
                            self.#col = Some(#col.clone());
                        } else {
                            set.insert(#col.clone());
                        }
                    }
                    other.#col = self.#col.clone();
                }
            })
            .collect();

        quote! {
            #[allow(clippy::mutable_key_type)]
            fn merge(&mut self, other: &mut Self) -> std::collections::HashSet<std::sync::Arc<Lock>> {
                let mut set = std::collections::HashSet::new();
                #(#rows)*
                set
            }
        }
    }
}