use crate::generator::config::Config;
use crate::generator::Column;
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use std::collections::HashMap;
#[derive(Default)]
pub struct HasOne {
pub has_one_use_plain: Vec<Vec<TokenStream>>,
pub has_one_use_impl: Vec<Vec<TokenStream>>,
pub has_one_field: Vec<TokenStream>,
pub has_one_reader: Vec<TokenStream>,
pub has_one_init: Vec<TokenStream>,
pub has_one_builder_impl: Vec<TokenStream>,
pub has_one_preload: Vec<TokenStream>,
}
pub fn make_has_one(
config: &Config,
self_builder_name: &Ident,
columns_map: &HashMap<String, Vec<Column>>,
configs: &[Config],
) -> HasOne {
let mut result: HasOne = HasOne::default();
let _table_name = config.table_name;
for has_one in config.has_one.iter() {
let child_config = configs
.iter()
.find(|x| x.struct_name == has_one.struct_name)
.unwrap();
let module_ident = format_ident!("{}", child_config.mod_name());
let module_impl_ident = format_ident!("{}_impl", module_ident);
let field_ident = format_ident!("{}", has_one.field);
let foreign_key_ident = format_ident!("{}", has_one.foreign_key);
let child_table_name = child_config.table_name;
let child_table_name_as = if has_one.field != child_config.mod_name() {
has_one.field
} else {
&child_table_name
};
let struct_ident = format_ident!("{}", has_one.struct_name);
let child_builder_ident = format_ident!("{}Builder", &struct_ident.to_string());
result
.has_one_use_plain
.push(vec![quote! ( use super::#module_ident::#struct_ident; )]);
result.has_one_use_impl.push(vec![
quote! ( use super::#module_ident::#struct_ident; ),
quote! ( use super::#module_impl_ident::#child_builder_ident; ),
]);
result
.has_one_field
.push(quote! { pub #field_ident: Option<Box<#struct_ident>>, });
result.has_one_reader.push(quote! {
pub fn #field_ident(&self) -> &#struct_ident {
self.#field_ident.as_ref().unwrap()
}
});
result.has_one_init.push(quote! { #field_ident: None, });
result.has_one_builder_impl.push(quote! {
pub fn #field_ident<F>(&self, f: F) -> #self_builder_name
where F: FnOnce(&#child_builder_ident) -> #child_builder_ident {
let mut builder = self.clone();
builder
.filters
.push(Filter::Builder(Box::new(f(&#child_builder_ident {
from: #child_table_name.to_string(),
table_name_as: Some(#child_table_name_as.to_string()),
relation_type: RelationType::HasOne(stringify!(#foreign_key_ident)),
..Default::default()
}))));
builder
}
});
let column = columns_map[child_table_name]
.iter()
.find(|column| column.name == has_one.foreign_key)
.unwrap();
let foreign_key_value = if column.is_nullable {
quote! { child.#foreign_key_ident.unwrap() }
} else {
quote! { child.#foreign_key_ident }
};
result.has_one_preload.push(quote! {
let builders = self.filters.iter().filter_map(|filter| match filter {
Filter::Builder(builder) if builder.table_name_as_or() == #child_table_name_as => Some(builder),
_ => None,
}).collect::<Vec<_>>();
if builders.iter().any(|x| x.preload()) {
let ids = result.iter().map(|x| x.id).collect::<Vec<_>>();
let mut children_builder = #struct_ident::select().#foreign_key_ident().r#in(ids);
for builder in &builders {
children_builder.filters.append(&mut builder.filters().clone());
children_builder.orders.append(&mut builder.order().clone());
if builder.group_by().is_some() {
children_builder.group_by = builder.group_by();
}
if builder.limit().is_some() {
children_builder.limit = builder.limit();
}
if builder.offset().is_some() {
children_builder.offset = builder.offset();
}
}
for filter in children_builder.filters.iter_mut() {
match filter {
Filter::Column(column) => {
column.table = #child_table_name.to_string();
column.preload = false;
}
_ => ()
}
}
let children = children_builder.load(conn).await?;
result.iter_mut().for_each(|x| {
for child in children.iter() {
if x.id == #foreign_key_value {
x.#field_ident = Some(Box::new(child.clone()));
break;
}
}
});
}
});
}
result
}