cellular_raza_core_derive/
lib.rs

1#![warn(missing_docs)]
2#![warn(clippy::missing_docs_in_private_items)]
3//! This crate provides powerful derive macros to automatically implement the `UpdateCycle` and `UpdateMechanics` traits.
4//! For the future, we are planning to have similar functionality with other concepts associated to CellAgents.
5
6use proc_macro::{TokenStream, TokenTree};
7use quote::{quote, ToTokens};
8use syn::{parse::ParseStream, parse_macro_input, spanned::Spanned, DeriveInput};
9
10#[proc_macro_derive(AuxStorage, attributes(UpdateCycle, UpdateMechanics))]
11/// Derives the `UpdateCycle` and `UpdateMechanics` trait automatically for the containing struct.
12pub fn aux_storage(input: TokenStream) -> TokenStream {
13    // Parse the input tokens into a syntax tree
14    let ast = parse_macro_input!(input as DeriveInput);
15
16    // Build the output, possibly using quasi-quotation
17    let struct_name = ast.ident;
18    let struct_generics = ast.generics.clone();
19    let mut result = TokenStream::new();
20
21    let data: syn::DataStruct = match ast.data {
22        syn::Data::Struct(data) => data,
23        _ => panic!("Usage of #[UpdateCycle] on a non-struct type"),
24    };
25    for field in data.fields.iter() {
26        // Update Cycle
27        if field.attrs.iter().any(|x| match &x.meta {
28            syn::Meta::Path(path) => path.is_ident("UpdateCycle"),
29            _ => false,
30        }) {
31            let name = &field.ident;
32            let res2 = quote! {
33                impl #struct_generics UpdateCycle for #struct_name #struct_generics {
34                    fn set_cycle_events(&mut self, events: Vec<CycleEvent>) {
35                        self.#name.set_cycle_events(events)
36                    }
37
38                    fn get_cycle_events(&self) -> Vec<CycleEvent> {
39                        self.#name.get_cycle_events()
40                    }
41
42                    fn add_cycle_event(&mut self, event: CycleEvent) {
43                        self.#name.add_cycle_event(event)
44                    }
45                }
46            };
47            result.extend(TokenStream::from(res2));
48        }
49        // Update Mechanics
50        else if field.attrs.iter().any(|x| match &x.meta {
51            syn::Meta::Path(path) => path.is_ident("UpdateMechanics"),
52            _ => false,
53        }) {
54            let name = &field.ident;
55            let generic_args = match &field.ty {
56                syn::Type::Path(path) => {
57                    path.path
58                        .segments
59                        .first()
60                        .and_then(|segment| match &segment.arguments {
61                            syn::PathArguments::AngleBracketed(arg) => {
62                                Some(arg.args.clone().into_iter().collect::<Vec<_>>())
63                            }
64                            _ => None,
65                        })
66                }
67                _ => None,
68            }
69            .or(Some(Vec::new()))
70            .unwrap();
71
72            let position_generic = generic_args[0].clone();
73            let velocity_generic = generic_args[1].clone();
74            let force_generic = generic_args[2].clone();
75
76            let res2 = quote! {
77                impl #struct_generics UpdateMechanics <#(#generic_args),*> for #struct_name #struct_generics
78                where
79                    F: Clone + core::ops::AddAssign<F> + num::Zero,
80                {
81                    fn set_last_position(&mut self, pos: #position_generic) {
82                        self.#name.set_last_position(pos)
83                    }
84                    fn previous_positions(&self) -> std::collections::vec_deque::Iter<#position_generic> {
85                        self.#name.previous_positions()
86                    }
87                    fn set_last_velocity(&mut self, vel: #velocity_generic) {
88                        self.#name.set_last_velocity(vel)
89                    }
90                    fn previous_velocities(&self) -> std::collections::vec_deque::Iter<#velocity_generic> {
91                        self.#name.previous_velocities()
92                    }
93                    fn add_force(&mut self, force: #force_generic) {
94                        self.#name.add_force(force);
95                    }
96                    fn get_current_force(&self) -> #force_generic {
97                        self.#name.get_current_force()
98                    }
99                    fn clear_forces(&mut self) {
100                        self.#name.clear_forces()
101                    }
102                }
103            };
104            result.extend(TokenStream::from(res2));
105        }
106    }
107
108    // Hand the output tokens back to the compiler
109    TokenStream::from(result)
110}
111
112fn parse_non_delimiter_tokens(tokenstream: TokenStream) -> Vec<TokenStream> {
113    use itertools::Itertools;
114    tokenstream
115        .into_iter()
116        .group_by(|token| match token {
117            TokenTree::Punct(p) => p.as_char() == char::from(','),
118            _ => false,
119        })
120        .into_iter()
121        .filter_map(|(is_comma, group)| if !is_comma { Some(group) } else { None })
122        .map(|group| TokenStream::from_iter(group.into_iter()))
123        .collect()
124}
125
126#[proc_macro]
127/// Simple macro that checks if two supplied values are identical.
128/// If this is the case it will insert the specified expression.
129/// Otherwise it inserts nothing.
130///
131/// The macro can be used to omit code when two idents are not identical.
132/// It can also be used to check if an identifier is contained in a range of identifiers.
133/// ```
134/// # use cellular_raza_core_derive::identical;
135/// // Identifiers are not matching. This means that
136/// // the last statement will never be inserted
137/// identical!(MyFirstIdentifier, MySecondIdentifier, assert!(false));
138///
139/// // Identifiers are matching. The last statement
140/// // (in this case `assert!(true)`) will be inserted into the code.
141/// identical!(SameIdent, SameIdent, assert!(true));
142///
143/// // The String "hamster" is inserted here since both identifiers are matching.
144/// assert_eq!("hamster", identical!(Id1, Id1, "hamster"));
145///
146/// // Identifiers are not equal if their capitalization does not match
147/// identical!(caps, Caps, assert!(false));
148///
149/// // This works since 1_f64 is turned into a string and then
150/// // compared to "1_f64" which is identically the same.
151/// identical!(1_f64, "1_f64", assert!(true));
152/// ```
153///
154/// The macro is not going to compile if given only two idents
155/// ```compile_fail
156/// identical!(Id1, Id2);
157/// ```
158///
159/// The same holds true if we do not supply two identifiers.
160/// ```compile_fail
161/// identical!(Id1, println!("asdf"));
162/// ```
163pub fn identical(tokenstream: TokenStream) -> TokenStream {
164    let tokens = parse_non_delimiter_tokens(tokenstream);
165    let tokens_length = tokens.len();
166    if tokens_length != 3 {
167        panic!("Macro requires two identifiers to compare against each other and one expression to insert");
168    } else {
169        let m1 = tokens[0].clone();
170        let m2 = tokens[1].clone();
171        let expr = TokenStream::from_iter(tokens.into_iter().skip(2).into_iter());
172        if m1.to_string() == m2.to_string() {
173            expr
174        } else {
175            TokenStream::from(quote!())
176        }
177    }
178}
179
180/* #[derive(Clone, Debug)]
181enum Aspect {
182    Mechanics,
183    Cycle,
184    Interaction,
185    CellularReactions,
186}
187
188fn element_to_aspect(element: syn::Expr) -> syn::Result<Aspect> {
189    match element.clone() {
190        syn::Expr::Path(path) => {
191            let ident: syn::Ident = path.path.segments.into_iter().next().unwrap().ident;
192            if ident == "Mechanics" {
193                Ok(Aspect::Mechanics)
194            } else if ident.to_string().to_lowercase() == "cycle" {
195                Ok(Aspect::Cycle)
196            } else if ident.to_string().to_lowercase() == "interaction" {
197                Ok(Aspect::Interaction)
198            } else if ident.to_string().to_lowercase() == "cellularreactions" {
199                Ok(Aspect::CellularReactions)
200            } else {
201                Err(syn::Error::new(
202                    element.span(),
203                    format!("Expected one of [Mechanics, Cycle, Interaction, CellularReactions]"),
204                ))
205            }
206        }
207        _ => Err(syn::Error::new(element.span(), "Expected expression here.")),
208    }
209}
210
211struct SimulationInformation {
212    setup: syn::Ident,
213    settings: syn::Ident,
214    aspects: Vec<Aspect>,
215}
216
217impl syn::parse::Parse for SimulationInformation {
218    fn parse(input: ParseStream) -> syn::parse::Result<Self> {
219        let setup: syn::Ident = input.parse()?;
220        let _: syn::token::Comma = input.parse()?;
221        let settings: syn::Ident = input.parse()?;
222        let _: syn::token::Comma = input.parse()?;
223        let aspects: syn::ExprArray = input.parse()?;
224
225        let aspects = aspects
226            .elems
227            .into_iter()
228            .map(element_to_aspect)
229            .collect::<syn::Result<Vec<_>>>()?;
230
231        Ok(Self {
232            setup,
233            settings,
234            aspects,
235        })
236    }
237}
238
239///
240#[proc_macro]
241pub fn run_full_simulation(input: TokenStream) -> TokenStream {
242    // let mut tokens = parse_non_delimiter_tokens(input).into_iter();
243    // let setup = tokens.next().unwrap();
244    // let settings = tokens.next().unwrap();
245    // let aspects = tokens.next().unwrap();
246
247    let SimulationInformation {
248        setup,
249        settings,
250        aspects,
251    } = parse_macro_input!(input as SimulationInformation);
252
253    TokenStream::from(quote!({
254        // TODO construct the aux storage from the simulation aspects
255        struct AuxStorage {}
256        1_u8
257    }))
258}*/