use proc_macro2::TokenStream;
use quote::quote;
use crate::common::model::Operation;
use crate::common::name_generator::WorktableNameGenerator;
use crate::generators::persist::PersistGenerator;
use convert_case::{Case, Casing};
use proc_macro2::{Ident, Span};
use std::collections::HashMap;
impl PersistGenerator {
pub fn gen_query_in_place_impl(&self) -> syn::Result<TokenStream> {
let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string());
let table_ident = name_generator.get_work_table_ident();
let custom_in_place = if let Some(q) = &self.queries {
let custom_in_place = self.gen_in_place_queries(q.in_place.clone());
quote! {
#custom_in_place
}
} else {
quote! {}
};
Ok(quote! {
impl #table_ident {
#custom_in_place
}
})
}
fn gen_in_place_queries(&self, in_place_queries: HashMap<Ident, Operation>) -> TokenStream {
let defs = in_place_queries
.iter()
.map(|(name, op)| {
let snake_case_name = name.to_string().from_case(Case::Pascal).to_case(Case::Snake);
let index = self.columns.indexes.values().find(|idx| idx.field == op.by);
if let Some(index) = index {
let _index_name = &index.name;
if index.is_unique { todo!() } else { todo!() }
} else if self.columns.primary_keys.len() == 1 {
self.gen_primary_key_in_place(snake_case_name, &op.columns)
} else {
todo!()
}
})
.collect::<Vec<_>>();
quote! {
#(#defs)*
}
}
fn gen_primary_key_in_place(&self, snake_case_name: String, columns: &[Ident]) -> TokenStream {
let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string());
let pk_type = name_generator.get_primary_key_type_ident();
let lock_ident = WorktableNameGenerator::get_update_in_place_query_lock_ident(&snake_case_name);
let method_ident = Ident::new(
format!("update_{snake_case_name}_in_place").as_str(),
Span::mixed_site(),
);
let types = columns
.iter()
.map(|c| self.columns.columns_map.get(c).unwrap())
.collect::<Vec<_>>();
let column_types = if types.len() == 1 {
let t = types[0];
quote! {
&mut <#t as rkyv::Archive>::Archived
}
} else {
let types = types.iter().map(|t| {
quote! {
&mut <#t as rkyv::Archive>::Archived
}
});
quote! {
( #(#types),* )
}
};
let column_fields = if columns.len() == 1 {
let i = &columns[0];
quote! {
&mut archived.inner.#i
}
} else {
let columns = columns.iter().map(|i| {
quote! {
&mut archived.inner.#i
}
});
quote! {
( #(#columns),* )
}
};
let custom_lock = self.gen_custom_lock_for_update(lock_ident);
quote! {
pub async fn #method_ident<Pk, F: FnMut(#column_types)>(
&self,
mut f: F,
by: Pk,
) -> eyre::Result<()>
where #pk_type: From<Pk>
{
let pk: #pk_type = by.into();
let op_lock = { #custom_lock };
let _guard = LockGuard::new(
op_lock,
self.0.lock_manager.clone(),
pk.clone(),
);
let link = self
.0
.primary_index.pk_map
.get(&pk)
.map(|v| v.get().value.into())
.ok_or(WorkTableError::NotFound)?;
unsafe {
self.0
.data
.with_mut_ref(link, move |archived| f(#column_fields))
.map_err(WorkTableError::PagesError)?
};
Ok(())
}
}
}
}