use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use valence_core::SchemaConnection;
use crate::codegen::schema::SchemaContext;
use super::common::connection_target_type;
pub(super) fn push_has_one_methods_for_connection(
schema: &SchemaContext,
struct_name: &proc_macro2::Ident,
conn: &SchemaConnection,
field_names: &[&str],
methods: &mut Vec<TokenStream>,
) {
let from_field = &conn.from_field;
if !field_names.contains(&from_field.as_str()) {
return;
}
let field_ident = format_ident!("{}", from_field);
let target_type = connection_target_type(conn);
let get_method_name = format_ident!("get_{}", from_field);
let thing_method_name = format_ident!("{}_thing", from_field);
let get_from_method_name = format_ident!("get_from_{}", from_field);
let get_from_id_method_name = format_ident!("get_from_{}_id", from_field);
let where_method_name = format_ident!("where_{}", from_field);
let field_is_nullable = schema
.fields
.iter()
.find(|f| f.name == *from_field)
.is_some_and(|f| f.nullable);
if field_is_nullable {
methods.push(quote! {
pub fn #thing_method_name(&self) -> Option<&valence::RecordId> {
self.#field_ident.as_ref()
}
});
} else {
methods.push(quote! {
pub fn #thing_method_name(&self) -> &valence::RecordId {
&self.#field_ident
}
});
}
let target_table_lit = conn.to_table.as_str();
let forward_method = if field_is_nullable {
quote! {
pub async fn #get_method_name(&self, valence: &valence::Valence) -> valence::Result<Option<#target_type>> {
match &self.#field_ident {
Some(rid) => {
let id = valence::connection::extract_id_from_record(rid)?;
<#target_type as valence::Model>::get(&id, valence).await
}
None => Ok(None),
}
}
}
} else if conn.required {
quote! {
pub async fn #get_method_name(&self, valence: &valence::Valence) -> valence::Result<#target_type> {
let id = valence::connection::extract_id_from_record(&self.#field_ident)?;
<#target_type as valence::Model>::get(&id, valence).await?
.ok_or_else(|| valence::Error::NotFound(
format!("{} {} not found", #target_table_lit, id),
))
}
}
} else {
quote! {
pub async fn #get_method_name(&self, valence: &valence::Valence) -> valence::Result<Option<#target_type>> {
let id = valence::connection::extract_id_from_record(&self.#field_ident)?;
<#target_type as valence::Model>::get(&id, valence).await
}
}
};
methods.push(forward_method);
methods.push(quote! {
pub async fn #get_from_method_name(
target: &#target_type,
valence: &valence::Valence,
) -> valence::Result<Vec<#struct_name>> {
let id = valence::connection::id_from_model(target)?;
Self::#get_from_id_method_name(&id, valence).await
}
});
let to_table_lit = conn.to_table.as_str();
methods.push(quote! {
pub async fn #get_from_id_method_name(
target_id: &str,
valence: &valence::Valence,
) -> valence::Result<Vec<#struct_name>> {
#struct_name::query(valence)
.#where_method_name(valence::RecordPredicate::Equals(
valence::RecordId::new(#to_table_lit, target_id),
))
.await
}
});
}