feos_derive/
lib.rs

1//! This crate provides derive macros used for the EosVariant and
2//! FunctionalVariant enums in FeOs. The macros implement
3//! the boilerplate for the EquationOfState and HelmholtzEnergyFunctional traits.
4#![warn(clippy::all)]
5use dft::expand_helmholtz_energy_functional;
6use functional_contribution::expand_functional_contribution;
7use ideal_gas::expand_ideal_gas;
8use proc_macro::TokenStream;
9use residual::expand_residual;
10use subset::expand_subset;
11use syn::{parse_macro_input, DeriveInput};
12
13mod dft;
14mod functional_contribution;
15mod ideal_gas;
16mod residual;
17mod subset;
18
19// possible additional traits to implement
20const OPT_IMPLS: [&str; 7] = [
21    "molar_weight",
22    "parameter_info",
23    "entropy_scaling",
24    "functional",
25    "bond_lengths",
26    "fluid_parameters",
27    "pair_potential",
28];
29
30fn implement(name: &str, variant: &syn::Variant, opts: &[&'static str]) -> syn::Result<bool> {
31    let syn::Variant { attrs, .. } = variant;
32    let mut implement = Ok(false);
33    for attr in attrs.iter() {
34        if attr.path.is_ident("implement") {
35            if let Ok(syn::Meta::List(list)) = attr.parse_meta() {
36                for meta in list.nested {
37                    if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = meta {
38                        // check if all keywords are valid, return error if not
39                        if !opts.iter().any(|s| path.is_ident(s)) {
40                            let opts = opts.join(", ");
41                            return Err(syn::Error::new_spanned(
42                                path,
43                                format!("expected one of: {opts}"),
44                            ));
45                        }
46
47                        // "name" is present
48                        if path.is_ident(name) {
49                            implement = Ok(true)
50                        }
51                    }
52                }
53            } else {
54                return Err(syn::Error::new_spanned(
55                    &attr.tokens,
56                    "expected 'implement(optional_trait, ...)'",
57                ));
58            }
59        }
60    }
61    implement
62}
63
64#[proc_macro_derive(Subset)]
65pub fn derive_components(input: TokenStream) -> TokenStream {
66    let input = parse_macro_input!(input as DeriveInput);
67    expand_subset(input)
68        .unwrap_or_else(syn::Error::into_compile_error)
69        .into()
70}
71
72#[proc_macro_derive(IdealGas)]
73pub fn derive_ideal_gas(input: TokenStream) -> TokenStream {
74    let input = parse_macro_input!(input as DeriveInput);
75    expand_ideal_gas(input)
76        .unwrap_or_else(syn::Error::into_compile_error)
77        .into()
78}
79
80#[proc_macro_derive(ResidualDyn, attributes(implement))]
81pub fn derive_residual(input: TokenStream) -> TokenStream {
82    let input = parse_macro_input!(input as DeriveInput);
83    expand_residual(input)
84        .unwrap_or_else(syn::Error::into_compile_error)
85        .into()
86}
87
88#[proc_macro_derive(HelmholtzEnergyFunctionalDyn, attributes(implement))]
89pub fn derive_helmholtz_energy_functional(input: TokenStream) -> TokenStream {
90    let input = parse_macro_input!(input as DeriveInput);
91    expand_helmholtz_energy_functional(input)
92        .unwrap_or_else(syn::Error::into_compile_error)
93        .into()
94}
95
96#[proc_macro_derive(FunctionalContribution)]
97pub fn derive_functional_contribution(input: TokenStream) -> TokenStream {
98    let input = parse_macro_input!(input as DeriveInput);
99    expand_functional_contribution(input)
100        .unwrap_or_else(syn::Error::into_compile_error)
101        .into()
102}