bourse_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3
4extern crate self as bourse_de;
5
6/// Agent iteration macro
7///
8/// Implements the `AgentSet` trait for a struct
9/// with fields of agent types. It's often the case
10/// we want to implement `update` function that
11/// iterates over a heterogeneous set of agents,
12/// which this macro automates. For example
13///
14/// ```no_rust
15/// #[derive(Agents)]
16/// struct SimAgents {
17///     a: AgentTypeA,
18///     b: AgentTypeB,
19/// }
20/// ```
21///
22/// expands to
23///
24/// ```no_rust
25/// struct SimAgents {
26///     a: AgentTypeA,
27///     b: AgentTypeB,
28/// }
29///
30/// impl AgentSet for SimAgents {
31///     fn update<R: RngCore>(
32///         &mut self, env: &mut Env, rng: &mut R
33///     ) {
34///         self.a.update(env, rng);
35///         self.b.update(env, rng);
36///     }
37/// }
38/// ```
39///
40#[proc_macro_derive(Agents)]
41pub fn agents_derive(input: TokenStream) -> TokenStream {
42    let ast = syn::parse(input).unwrap();
43    impl_agents_macro(&ast)
44}
45
46fn impl_agents_macro(ast: &syn::DeriveInput) -> TokenStream {
47    let name = &ast.ident;
48
49    let fields = match &ast.data {
50        syn::Data::Struct(syn::DataStruct {
51            fields: syn::Fields::Named(fields),
52            ..
53        }) => &fields.named,
54        _ => panic!("expected a struct with named fields"),
55    };
56
57    let mut call_tokens = quote!();
58
59    for field in fields {
60        let field_name = field.ident.clone();
61
62        if field_name.is_some() {
63            call_tokens.extend(quote!(
64                self.#field_name.update(env, rng);
65            ));
66        }
67    }
68
69    let output = quote! {
70        impl bourse_de::agents::AgentSet for #name {
71            fn update<R: rand::RngCore>(&mut self, env: &mut bourse_de::Env, rng: &mut R) {
72                #call_tokens
73            }
74        }
75    };
76
77    TokenStream::from(output)
78}