cellular_raza_core_proc_macro/
testing.rs1use itertools::Itertools;
2use quote::quote;
3
4use super::simulation_aspects::{SimulationAspect, SimulationAspects};
5
6#[allow(unused)]
7struct Sorted {
8 sorted_kw: syn::Ident,
9 colon: syn::Token![:],
10 sorted: bool,
11}
12
13impl syn::parse::Parse for Sorted {
14 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
15 Ok(Self {
16 sorted_kw: input.parse()?,
17 colon: input.parse()?,
18 sorted: input.parse::<syn::LitBool>()?.value,
19 })
20 }
21}
22
23#[allow(unused)]
24struct MacroParser {
25 test_token: syn::Ident,
26 colon: syn::Token![:],
27 macro_name: syn::Ident,
28 comma: syn::Token![,],
29 aspects: SimulationAspects,
30 min_combinations: Option<core::num::NonZeroUsize>,
31 sorted: Option<bool>,
32}
33
34impl syn::parse::Parse for MacroParser {
35 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
36 let mut res = Self {
37 test_token: input.parse()?,
38 colon: input.parse()?,
39 macro_name: input.parse()?,
40 comma: input.parse()?,
41 aspects: input.parse()?,
42 min_combinations: None,
43 sorted: None,
44 };
45 while !input.is_empty() {
46 let _: syn::Token![,] = input.parse()?;
47 if !input.is_empty() {
48 let keyword: syn::Ident = input.parse()?;
49 let _: syn::Token![:] = input.parse()?;
50 match keyword.to_string().as_ref() {
51 "min_combinations" => {
52 res.min_combinations = Some(input.parse::<syn::LitInt>()?.base10_parse()?)
53 }
54 "sorted" => res.sorted = Some(input.parse::<syn::LitBool>()?.value),
55 _ => (),
56 }
57 }
58 }
59 Ok(res)
60 }
61}
62
63impl MacroParser {
64 fn spawn_tests(self) -> proc_macro2::TokenStream {
65 let macro_name = &self.macro_name;
66 let aspects: Vec<_> = self.aspects.to_aspect_list();
67 let min_order = self.min_combinations.map(|x| x.get()).unwrap_or(1);
68 let sorted = self.sorted.map(|x| x).unwrap_or(true);
69
70 let mut stream = quote!();
71 for n in min_order..aspects.len() {
72 let combinations = get_combinations(n, aspects.clone(), sorted);
73
74 for (name, list) in combinations {
75 let list_aspects = list.into_iter().map(|aspect| aspect.to_token_stream());
76 let output = quote!(
77 #macro_name !(
78 name:#name,
79 aspects:[#(#list_aspects),*]
80 );
81 );
82 stream.extend(output);
83 }
84 }
85 stream
86 }
87}
88
89pub fn run_test_for_aspects(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
90 let macro_parser = syn::parse_macro_input!(input as MacroParser);
91 macro_parser.spawn_tests().into()
92}
93
94fn idents_overlap(id1: &proc_macro2::TokenStream, id2: &proc_macro2::TokenStream) -> bool {
95 let id1_segments = id1.to_string();
96 let id1_segments = id1_segments.split("_").collect::<Vec<_>>();
97 let id2_segments = id2.to_string();
98 let id2_segments = id2_segments.split("_").collect::<Vec<_>>();
99 let l1 = id1_segments.len();
100 let l2 = id2_segments.len();
101 let set = std::collections::HashSet::<&str>::from_iter(
102 id1_segments.into_iter().chain(id2_segments.into_iter()),
103 );
104 set.len() < l1 + l2
105}
106
107fn get_combinations(
108 n: usize,
109 idents: Vec<SimulationAspect>,
110 sorted: bool,
111) -> Vec<(proc_macro2::TokenStream, Vec<SimulationAspect>)> {
112 let idents: Vec<(proc_macro2::TokenStream, Vec<SimulationAspect>)> = idents
113 .into_iter()
114 .map(|s| (s.to_token_stream_lowercase(), vec![s]))
115 .collect();
116
117 if n == 0 {
118 return idents;
119 }
120
121 fn combine_idents(
122 ident1: &(proc_macro2::TokenStream, Vec<SimulationAspect>),
123 ident2: &(proc_macro2::TokenStream, Vec<SimulationAspect>),
124 ) -> Option<(proc_macro2::TokenStream, Vec<SimulationAspect>)> {
125 if idents_overlap(&ident1.0, &ident2.0) {
126 return None;
127 }
128 let i1 = &ident1.0;
129 let i2 = &ident2.0;
130 let name_ident = quote::format_ident!("{}_{}", i1.to_string(), i2.to_string());
131 let name = quote!(#name_ident);
132 let mut list = ident1.1.clone();
133 let list2 = ident2.1.clone();
134 list.extend(list2);
135 let list = list.into_iter().map(|s| s.into()).collect();
136 Some((name, list))
137 }
138
139 if sorted {
140 return idents
141 .iter()
142 .combinations(n)
143 .into_iter()
144 .map(|ids| {
145 ids.into_iter()
146 .fold((quote::quote!(), vec![]), |mut acc, x| {
147 if acc.1.is_empty() {
148 acc = x.clone();
149 return acc;
150 }
151 match combine_idents(&acc, x) {
152 Some(res) => {
153 acc = res;
154 acc
155 }
156 None => acc,
157 }
158 })
159 })
160 .collect();
161 }
162
163 let combinations: Vec<_> = (1..n).fold(
164 idents
165 .iter()
166 .map(|ident1| {
167 idents
168 .iter()
169 .filter_map(move |ident2| combine_idents(ident1, ident2))
170 })
171 .flatten()
172 .collect(),
173 |acc, _| {
174 acc.iter()
175 .map(|ident1| {
176 idents
177 .iter()
178 .filter_map(move |ident2| combine_idents(ident1, ident2))
179 })
180 .flatten()
181 .collect()
182 },
183 );
184 combinations
185}