use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::Result;
use super::graph_decl::{UpdateGraphDeclarations, UpdateStrategy};
pub(super) fn generate_has_many_update_code(
graph: &UpdateGraphDeclarations,
_table_name: &str,
) -> Result<TokenStream> {
if graph.has_many.is_empty() {
return Ok(quote! {});
}
let mut code_blocks = Vec::new();
for rel in &graph.has_many {
let field_ident = format_ident!("{}", rel.field);
let child_type = &rel.child_type;
let fk_column = &rel.fk_column;
let setter_name = format_ident!("with_{}", rel.fk_field);
let strategy_code = match rel.strategy {
UpdateStrategy::Replace => {
quote! {
let delete_sql = ::std::format!(
"DELETE FROM {} WHERE {} = $1",
#child_type::TABLE,
#fk_column
);
let deleted = ::pgorm::query(delete_sql).bind(__pgorm_id.clone()).execute(conn).await?;
__pgorm_total_affected += deleted;
if !children.is_empty() {
let children_with_fk: ::std::vec::Vec<_> = children
.into_iter()
.map(|child| child.#setter_name(__pgorm_id.clone()))
.collect();
let inserted = #child_type::insert_many(conn, children_with_fk).await?;
__pgorm_total_affected += inserted;
}
}
}
UpdateStrategy::Append => {
quote! {
if !children.is_empty() {
let children_with_fk: ::std::vec::Vec<_> = children
.into_iter()
.map(|child| child.#setter_name(__pgorm_id.clone()))
.collect();
let inserted = #child_type::insert_many(conn, children_with_fk).await?;
__pgorm_total_affected += inserted;
}
}
}
UpdateStrategy::Upsert => {
quote! {
if !children.is_empty() {
let children_with_fk: ::std::vec::Vec<_> = children
.into_iter()
.map(|child| child.#setter_name(__pgorm_id.clone()))
.collect();
let upserted = #child_type::upsert_many(conn, children_with_fk).await?;
__pgorm_total_affected += upserted;
}
}
}
UpdateStrategy::Diff => {
let key_columns_vec: Vec<String> = rel.key_columns.as_ref().unwrap().clone();
quote! {
let diff_affected = #child_type::__pgorm_diff_many_by_fk(
conn,
#fk_column,
__pgorm_id.clone(),
&[#(#key_columns_vec),*],
children
.into_iter()
.map(|child| child.#setter_name(__pgorm_id.clone()))
.collect(),
).await?;
__pgorm_total_affected += diff_affected;
}
}
};
let code = quote! {
if let ::std::option::Option::Some(children) = self.#field_ident {
#strategy_code
}
};
code_blocks.push(code);
}
Ok(quote! {
#(#code_blocks)*
})
}
pub(super) fn generate_has_one_update_code(
graph: &UpdateGraphDeclarations,
_table_name: &str,
) -> Result<TokenStream> {
if graph.has_one.is_empty() {
return Ok(quote! {});
}
let mut code_blocks = Vec::new();
for rel in &graph.has_one {
let field_ident = format_ident!("{}", rel.field);
let child_type = &rel.child_type;
let fk_column = &rel.fk_column;
let setter_name = format_ident!("with_{}", rel.fk_field);
let strategy_code = match rel.strategy {
UpdateStrategy::Replace => {
quote! {
let delete_sql = ::std::format!(
"DELETE FROM {} WHERE {} = $1",
#child_type::TABLE,
#fk_column
);
let deleted = ::pgorm::query(delete_sql).bind(__pgorm_id.clone()).execute(conn).await?;
__pgorm_total_affected += deleted;
if let ::std::option::Option::Some(child) = inner_value {
let child_with_fk = child.#setter_name(__pgorm_id.clone());
let inserted = child_with_fk.insert(conn).await?;
__pgorm_total_affected += inserted;
}
}
}
UpdateStrategy::Upsert => {
quote! {
match inner_value {
::std::option::Option::None => {
let delete_sql = ::std::format!(
"DELETE FROM {} WHERE {} = $1",
#child_type::TABLE,
#fk_column
);
let deleted = ::pgorm::query(delete_sql).bind(__pgorm_id.clone()).execute(conn).await?;
__pgorm_total_affected += deleted;
}
::std::option::Option::Some(child) => {
let child_with_fk = child.#setter_name(__pgorm_id.clone());
let upserted = child_with_fk.upsert(conn).await?;
__pgorm_total_affected += upserted;
}
}
}
}
_ => {
quote! {}
}
};
let code = quote! {
if let ::std::option::Option::Some(inner_value) = self.#field_ident {
#strategy_code
}
};
code_blocks.push(code);
}
Ok(quote! {
#(#code_blocks)*
})
}