use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned};
use syn::{Data, DeriveInput, Fields, GenericParam, Ident, Index, parse_quote, spanned::Spanned};
pub(crate) fn site_pair_energy(input: DeriveInput) -> TokenStream {
let name = &input.ident;
let data = match input.data {
Data::Struct(data) => data,
Data::Enum(_) | Data::Union(_) => {
return quote_spanned! {
name.span() =>
compile_error!("derive(SitePairEnergy) applies only to struct types.");
};
}
};
let mut generics = input.generics.clone();
let s_ident = Ident::new("__S", Span::call_site());
generics.params = [GenericParam::Type(s_ident.into())]
.into_iter()
.chain(generics.params)
.collect();
let field_types = data.fields.iter().map(|f| f.ty.clone());
if let Some(previous_where_clause) = generics.where_clause {
let predicates = previous_where_clause.predicates;
generics.where_clause = Some(parse_quote!(where
#predicates,
#(#field_types: ::hoomd_interaction::SitePairEnergy<__S>),*
));
} else {
generics.where_clause = Some(parse_quote!(where
#(#field_types: ::hoomd_interaction::SitePairEnergy<__S>),*));
}
let (impl_generics, _, where_clause) = generics.split_for_impl();
let (_, ty_generics, _) = input.generics.split_for_impl();
let site_pair_energy_sum = site_pair_energy_sum(&data.fields);
let site_pair_energy_initial_sum = site_pair_energy_initial_sum(&data.fields);
let is_only_infinite_or_zero = is_only_infinite_or_zero(&data.fields);
let generated = quote! {
impl #impl_generics ::hoomd_interaction::SitePairEnergy<__S> for #name #ty_generics #where_clause {
#[inline]
fn site_pair_energy(&self,
site_properties_i: &__S,
site_properties_j: &__S) -> f64 {
#site_pair_energy_sum
}
#[inline]
fn site_pair_energy_initial(&self,
site_properties_i: &__S,
site_properties_j: &__S) -> f64 {
#site_pair_energy_initial_sum
}
#[inline]
fn is_only_infinite_or_zero() -> bool {
#is_only_infinite_or_zero
}
}
};
generated
}
fn site_pair_energy_sum(fields: &Fields) -> TokenStream {
match fields {
Fields::Named(fields) => {
let terms = fields.named.iter().map(|f| {
let name = &f.ident;
quote_spanned! {f.span()=>
::hoomd_interaction::SitePairEnergy::site_pair_energy(&self.#name,
site_properties_i, site_properties_j)
}
});
quote! {
let mut total = 0.0_f64;
#(
total += #terms;
if total == f64::INFINITY {
return total;
}
)*
total
}
}
Fields::Unnamed(fields) => {
let terms = fields.unnamed.iter().enumerate().map(|(i, f)| {
let index = Index::from(i);
quote_spanned! {f.span()=>
::hoomd_interaction::SitePairEnergy::site_pair_energy(&self.#index,
site_properties_i, site_properties_j)
}
});
quote! {
let mut total = 0.0_f64;
#(
total += #terms;
if total == f64::INFINITY {
return total;
}
)*
total
}
}
Fields::Unit => {
quote!(0.0_f64)
}
}
}
fn site_pair_energy_initial_sum(fields: &Fields) -> TokenStream {
match fields {
Fields::Named(fields) => {
let terms = fields.named.iter().map(|f| {
let name = &f.ident;
quote_spanned! {f.span()=>
::hoomd_interaction::SitePairEnergy::site_pair_energy_initial(&self.#name,
site_properties_i, site_properties_j)
}
});
quote! {
let mut total = 0.0_f64;
#(
total += #terms;
if total == f64::INFINITY {
return total;
}
)*
total
}
}
Fields::Unnamed(fields) => {
let terms = fields.unnamed.iter().enumerate().map(|(i, f)| {
let index = Index::from(i);
quote_spanned! {f.span()=>
::hoomd_interaction::SitePairEnergy::site_pair_energy_initial(&self.#index,
site_properties_i, site_properties_j)
}
});
quote! {
let mut total = 0.0_f64;
#(
total += #terms;
if total == f64::INFINITY {
return total;
}
)*
total
}
}
Fields::Unit => {
quote!(0.0_f64)
}
}
}
fn is_only_infinite_or_zero(fields: &Fields) -> TokenStream {
match fields {
Fields::Named(fields) => {
let terms = fields.named.iter().map(|f| {
let ty = &f.ty;
quote_spanned! {f.span()=>
<#ty as ::hoomd_interaction::SitePairEnergy<__S>>::is_only_infinite_or_zero()
}
});
quote! {
#(#terms)&&*
}
}
Fields::Unnamed(fields) => {
let terms = fields.unnamed.iter().map(|f| {
let ty = &f.ty;
quote_spanned! {f.span()=>
#ty::is_only_infinite_or_zero()
}
});
quote! {
#(#terms)&&*
}
}
Fields::Unit => {
quote!(0.0_f64)
}
}
}