cellular_raza_core_proc_macro/lib.rs
1#![deny(missing_docs)]
2//! This crate provides powerful macros to derive traits from the core crate.
3//! It also provides macros to automatically construct AuxStorage structs
4//! used to store intermediate data for running update steps and Communicator
5//! struct to send messages between threads running the simulation.
6//!
7//! All macros are documented in the core crate unless their functionality can be
8//! displayed without any additional dependencies.
9
10#[macro_use]
11mod kwargs;
12
13mod aux_storage;
14mod communicator;
15mod from_map;
16mod run_sim;
17mod simulation_aspects;
18mod testing;
19
20#[allow(missing_docs)]
21#[proc_macro_derive(
22 AuxStorage,
23 attributes(
24 AuxStorageCorePath,
25 UpdateCycle,
26 UpdateMechanics,
27 UpdateNeighborSensing,
28 UpdateReactions,
29 UpdateReactionsContact,
30 )
31)]
32pub fn _aux_storage(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
33 aux_storage::derive_aux_storage(input)
34}
35
36#[allow(missing_docs)]
37#[proc_macro]
38pub fn build_aux_storage(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
39 let kwargs = syn::parse_macro_input!(input as aux_storage::KwargsAuxStorageParsed);
40 let kwargs = aux_storage::KwargsAuxStorage::from(kwargs);
41 aux_storage::construct_aux_storage(kwargs)
42}
43
44#[allow(missing_docs)]
45#[proc_macro]
46pub fn aux_storage_constructor(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
47 let kwargs = syn::parse_macro_input!(input as aux_storage::KwargsAuxStorageParsed);
48 let kwargs = aux_storage::KwargsAuxStorage::from(kwargs);
49 let res = aux_storage::default_aux_storage_initializer(&kwargs);
50 quote::quote!(#res).into()
51}
52
53#[allow(missing_docs)]
54#[proc_macro_derive(Communicator, attributes(CommunicatorCorePath, Comm))]
55pub fn _communicator(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
56 communicator::derive_communicator(input)
57}
58
59#[allow(missing_docs)]
60#[proc_macro]
61pub fn build_communicator(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
62 communicator::construct_communicator(input)
63}
64
65/// Inserts as many blanks as generics were used to create the communicator struct by
66/// [build_communicator!].
67#[proc_macro]
68pub fn communicator_generics_placeholders(
69 input: proc_macro::TokenStream,
70) -> proc_macro::TokenStream {
71 communicator::communicator_generics_placeholders(input)
72}
73
74#[allow(missing_docs)]
75#[proc_macro_derive(FromMap, attributes(FromMapCorePath, FromMapIndex))]
76pub fn from_map(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
77 from_map::derive_from_map(input)
78}
79
80#[proc_macro]
81/// Run a particularly structured test multiple times for combinations of aspects
82///
83/// The tests which we would like to run are macros that will
84/// be given as one argument to this `proc_macro`.
85/// These tests need to adhere to a strict format.
86/// ```
87/// macro_rules! some_test(
88/// (
89/// name:$test_name:ident,
90/// aspects:[$($asp:ident),*]
91/// ) => {
92/// // Any code can be run here.
93/// // For example, we can create a docstring test by using
94///
95/// /// ```
96/// /// assert_eq!(0_usize, 10_usize - 10_usize);
97/// $(#[doc = concat!("println!(\"", stringify!($asp), "\")")])*
98/// /// ```
99/// fn $test_name () {}
100/// }
101/// );
102///
103/// // This is how you would call the test by hand
104///
105/// some_test!(
106/// name:my_test_name,
107/// aspects: [Mechanics, Interaction]
108/// );
109/// ```
110///
111/// In the next step, we can use `run_test_for_aspects` to run this automatically generated
112/// docstring test for every combination of aspects that we specify.
113///
114/// ```
115/// # macro_rules! some_test(
116/// # (
117/// # name:$test_name:ident,
118/// # aspects:[$($asp:ident),*]
119/// # ) => {
120/// # // Any code can be run here.
121/// # // For example, we can create a docstring test by using
122/// #
123/// # /// ```
124/// # /// assert_eq!(0_usize, 10_usize - 10_usize);
125/// # $(#[doc = concat!("println!(\"", stringify!($asp), "\")")])*
126/// # /// ```
127/// # fn $test_name () {}
128/// # }
129/// # );
130/// # use cellular_raza_core_proc_macro::run_test_for_aspects;
131/// run_test_for_aspects!(
132/// test: some_test,
133/// aspects: [Mechanics, Interaction]
134/// );
135/// ```
136/// This will have generated the following code:
137/// ```
138/// # macro_rules! some_test(
139/// # (
140/// # name:$test_name:ident,
141/// # aspects:[$($asp:ident),*]
142/// # ) => {
143/// # // Any code can be run here.
144/// # // For example, we can create a docstring test by using
145/// #
146/// # /// ```
147/// # /// assert_eq!(0_usize, 10_usize - 10_usize);
148/// # $(#[doc = concat!("println!(\"", stringify!($asp), "\")")])*
149/// # /// ```
150/// # fn $test_name () {}
151/// # }
152/// # );
153/// some_test!(
154/// name:mechanics,
155/// aspects: [Mechanics]
156/// );
157/// some_test!(
158/// name:interaction,
159/// aspects: [Interaction]
160/// );
161/// some_test!(
162/// name:mechanics_interaction,
163/// aspects: [Mechanics, Interaction]
164/// );
165/// some_test!(
166/// name:interaction_mechanics,
167/// aspects: [Interaction, Mechanics]
168/// );
169/// ```
170///
171/// # Minimum Combinations
172/// It is possible to specify a minimum number of combinations to test.
173/// This means if we specify N aspects but only want to test combinations of M (where M<N)
174/// different aspects, we can set the `min_combinations` variable of this macro.
175///
176/// ```
177/// # macro_rules! some_test(
178/// # (
179/// # name:$test_name:ident,
180/// # aspects:[$($asp:ident),*]
181/// # ) => {
182/// # // Any code can be run here.
183/// # // For example, we can create a docstring test by using
184/// #
185/// # /// ```
186/// # /// assert_eq!(0_usize, 10_usize - 10_usize);
187/// # $(#[doc = concat!("println!(\"", stringify!($asp), "\")")])*
188/// # /// ```
189/// # fn $test_name () {}
190/// # }
191/// # );
192/// # use cellular_raza_core_proc_macro::run_test_for_aspects;
193/// run_test_for_aspects!(
194/// test: some_test,
195/// aspects: [Mechanics, Interaction, Cycle, Reactions],
196/// min_combinations: 3,
197/// );
198/// ```
199///
200/// # Unsorted Combinations
201/// By default all generated combinations of simulation aspects are sorted and will thus not
202/// produce different tests when being reordered.
203/// This means we assume that `aspects: [Mechanics, Interaction]` is identical to `aspects:
204/// [Interaction, Mechanics]`.
205/// In the case where we also want to test the unsorted cases, we can specify the `sorted` keyword.
206///
207/// ```
208/// # macro_rules! some_test(
209/// # (
210/// # name:$test_name:ident,
211/// # aspects:[$($asp:ident),*]
212/// # ) => {
213/// # // Any code can be run here.
214/// # // For example, we can create a docstring test by using
215/// #
216/// # /// ```
217/// # /// assert_eq!(0_usize, 10_usize - 10_usize);
218/// # $(#[doc = concat!("println!(\"", stringify!($asp), "\")")])*
219/// # /// ```
220/// # fn $test_name () {}
221/// # }
222/// # );
223/// # use cellular_raza_core_proc_macro::run_test_for_aspects;
224/// run_test_for_aspects!(
225/// test: some_test,
226/// aspects: [Mechanics, Interaction],
227/// sorted: false,
228/// );
229/// ```
230pub fn run_test_for_aspects(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
231 testing::run_test_for_aspects(input)
232}
233
234#[allow(missing_docs)]
235#[proc_macro]
236pub fn run_simulation(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
237 let kwargs = syn::parse_macro_input!(input as run_sim::KwargsSimParsed);
238 let kwargs = run_sim::KwargsSim::from(kwargs);
239 run_sim::run_simulation(kwargs).into()
240}
241
242/// Prepares communicator and auxiliary storage types with [build_communicator!] and
243/// [build_aux_storage!].
244#[proc_macro]
245pub fn prepare_types(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
246 let kwargs = syn::parse_macro_input!(input as run_sim::KwargsPrepareTypesParsed);
247 let kwargs = run_sim::KwargsPrepareTypes::from(kwargs);
248 run_sim::prepare_types(kwargs).into()
249}
250
251/// Checks if defined types and concepts are compatible before actually executing the simulation.
252///
253/// This macro only serves the purpose for easy-to-read compiler errors.
254/// It has no runtime-overhead since it will be fully optimized away.
255#[proc_macro]
256pub fn test_compatibility(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
257 let kwargs = syn::parse_macro_input!(input as run_sim::KwargsCompatibilityParsed);
258 let kwargs = run_sim::KwargsCompatibility::from(kwargs);
259 run_sim::test_compatibility(kwargs).into()
260}
261
262#[allow(missing_docs)]
263#[proc_macro]
264pub fn run_main(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
265 let kwargs = syn::parse_macro_input!(input as run_sim::KwargsMainParsed);
266 let kwargs = run_sim::KwargsMain::from(kwargs);
267 run_sim::run_main(kwargs).into()
268}