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}