foundationdb_macros/
lib.rs1use proc_macro::TokenStream;
3use quote::quote;
4use std::collections::HashMap;
5use syn::__private::TokenStream2;
6use syn::parse::Parser;
7use syn::{Item, ItemFn, LitInt};
8use try_map::FallibleMapExt;
9
10#[proc_macro_attribute]
23pub fn cfg_api_versions(args: TokenStream, input: TokenStream) -> TokenStream {
24 let args = proc_macro2::TokenStream::from(args);
25 let input = proc_macro2::TokenStream::from(input);
26 cfg_api_versions_impl(args, input).into()
27}
28
29fn cfg_api_versions_impl(
30 args: proc_macro2::TokenStream,
31 input: proc_macro2::TokenStream,
32) -> proc_macro2::TokenStream {
33 let mut min: Option<LitInt> = None;
34 let mut max: Option<LitInt> = None;
35 let version_parser = syn::meta::parser(|meta| {
36 if meta.path.is_ident("min") {
37 min = Some(meta.value()?.parse()?);
38 Ok(())
39 } else if meta.path.is_ident("max") {
40 max = Some(meta.value()?.parse()?);
41 Ok(())
42 } else {
43 Err(meta.error("unsupported cfg_api_versions property"))
44 }
45 });
46
47 Parser::parse2(version_parser, args).expect("Unable to parse attribute cfg_api_versions");
48
49 let input: Item = syn::parse2(input).expect("Unable to parse input");
50
51 let minimum_version = min
52 .expect("min property must be provided")
53 .base10_parse::<i32>()
54 .expect("Unable to parse min version");
55 let maximum_version = max
56 .try_map(|x| x.base10_parse::<i32>())
57 .expect("Unable to parse max version");
58 generate_feature_range(&input, minimum_version, maximum_version)
59}
60
61fn generate_feature_range(
62 input: &Item,
63 minimum_version: i32,
64 maximum_version: Option<i32>,
65) -> proc_macro2::TokenStream {
66 let allowed_fdb_versions: Vec<TokenStream2> =
67 get_supported_feature_range(minimum_version, maximum_version)
68 .iter()
69 .map(|fdb_version| quote!(feature = #fdb_version))
70 .collect();
71
72 quote!(
73 #[cfg(any(#(#allowed_fdb_versions),*))]
74 #input
75 )
76}
77
78fn get_supported_feature_range(minimum_version: i32, maximum_version: Option<i32>) -> Vec<String> {
80 let mut values: Vec<String> = get_version_mapping()
81 .iter()
82 .filter(|(_, version)| match maximum_version {
83 None => minimum_version <= **version,
84 Some(maximum) => minimum_version <= **version && version <= &&maximum,
85 })
86 .map(|(feature, _)| feature.to_owned())
87 .collect();
88 values.sort();
89
90 values
91}
92
93fn get_version_mapping() -> HashMap<String, i32> {
95 let mut version_mapping = HashMap::with_capacity(9);
96 version_mapping.insert("fdb-7_3".into(), 730);
97 version_mapping.insert("fdb-7_1".into(), 710);
98 version_mapping.insert("fdb-7_0".into(), 700);
99 version_mapping.insert("fdb-6_3".into(), 630);
100 version_mapping.insert("fdb-6_2".into(), 620);
101 version_mapping.insert("fdb-6_1".into(), 610);
102 version_mapping.insert("fdb-6_0".into(), 600);
103 version_mapping.insert("fdb-5_2".into(), 520);
104 version_mapping.insert("fdb-5_1".into(), 510);
105 version_mapping.insert("fdb-5_0".into(), 500);
106 version_mapping
107}
108
109#[proc_macro_attribute]
110pub fn simulation_entrypoint(_attr: TokenStream, item: TokenStream) -> TokenStream {
111 let input = syn::parse_macro_input!(item as ItemFn);
112
113 let block = &input.block;
114 let attrs = &input.attrs;
115
116 quote::quote!(
120 #(#attrs)*
121 #[no_mangle]
122 fn workload_instantiate_hook(name: &str, context: WorkloadContext) -> Box<dyn RustWorkload> {
123 #block
124 }
125 #[no_mangle]
126 pub extern "C" fn workloadFactory(logger: *const u8) -> *const u8 {
127 unsafe { ::foundationdb_simulation::CPPWorkloadFactory(logger as *const _) }
128 }
129 )
130 .into()
131}
132
133#[cfg(test)]
134mod tests {
135 use crate::cfg_api_versions_impl;
136 use crate::get_supported_feature_range;
137 use proc_macro2::TokenStream;
138 use quote::quote;
139
140 #[test]
141 fn test_create_supported_list() {
142 let v = get_supported_feature_range(700, None);
143 assert_eq!(v.len(), 3);
144 assert!(v.contains(&String::from("fdb-7_0")));
145 assert!(v.contains(&String::from("fdb-7_1")));
146 assert!(v.contains(&String::from("fdb-7_3")));
147
148 let v = get_supported_feature_range(600, Some(700));
149 assert_eq!(v.len(), 5);
150 assert!(v.contains(&String::from("fdb-7_0")));
151 assert!(v.contains(&String::from("fdb-6_3")));
152 assert!(v.contains(&String::from("fdb-6_2")));
153 assert!(v.contains(&String::from("fdb-6_1")));
154 assert!(v.contains(&String::from("fdb-6_0")));
155
156 let v = get_supported_feature_range(500, Some(610));
157 assert_eq!(v.len(), 5);
158 assert!(v.contains(&String::from("fdb-6_1")));
159 assert!(v.contains(&String::from("fdb-6_0")));
160 assert!(v.contains(&String::from("fdb-5_2")));
161 assert!(v.contains(&String::from("fdb-5_1")));
162 assert!(v.contains(&String::from("fdb-5_0")));
163
164 let v = get_supported_feature_range(500, None);
165 assert_eq!(v.len(), 10);
166 assert!(v.contains(&String::from("fdb-7_3")));
167 assert!(v.contains(&String::from("fdb-7_1")));
168 assert!(v.contains(&String::from("fdb-7_0")));
169 assert!(v.contains(&String::from("fdb-6_3")));
170 assert!(v.contains(&String::from("fdb-6_2")));
171 assert!(v.contains(&String::from("fdb-6_1")));
172 assert!(v.contains(&String::from("fdb-6_0")));
173 assert!(v.contains(&String::from("fdb-5_2")));
174 assert!(v.contains(&String::from("fdb-5_1")));
175 assert!(v.contains(&String::from("fdb-5_0")));
176 }
177
178 fn test_cfg_versions(expected_versions: TokenStream, attrs: TokenStream) {
179 let input = quote! {
180 fn ma_fonction() {}
181 };
182
183 let expected = quote! {
184 #[cfg(any(#expected_versions))]
185 fn ma_fonction() {}
186 };
187
188 let result = cfg_api_versions_impl(attrs, input);
189 assert_eq!(result.to_string(), expected.to_string())
190 }
191
192 #[test]
193 fn test_min_700_no_max_version() {
194 let data = quote!(
195 feature = "fdb-7_0",
196 feature = "fdb-7_1",
197 feature = "fdb-7_3"
198 );
199
200 let attrs = quote!(min = 700);
201
202 test_cfg_versions(data, attrs)
203 }
204
205 #[test]
206 fn test_min_600_max_700() {
207 let expected_versions = quote!(
208 feature = "fdb-6_0",
209 feature = "fdb-6_1",
210 feature = "fdb-6_2",
211 feature = "fdb-6_3",
212 feature = "fdb-7_0"
213 );
214
215 let attrs = quote!(min = 600, max = 700);
216
217 test_cfg_versions(expected_versions, attrs)
218 }
219
220 #[test]
221 fn test_min_500_max_610() {
222 let expected_versions = quote!(
223 feature = "fdb-5_0",
224 feature = "fdb-5_1",
225 feature = "fdb-5_2",
226 feature = "fdb-6_0",
227 feature = "fdb-6_1"
228 );
229
230 let attrs = quote!(min = 500, max = 610);
231
232 test_cfg_versions(expected_versions, attrs)
233 }
234
235 #[test]
236 fn test_min_500_no_max() {
237 let expected_versions = quote!(
238 feature = "fdb-5_0",
239 feature = "fdb-5_1",
240 feature = "fdb-5_2",
241 feature = "fdb-6_0",
242 feature = "fdb-6_1",
243 feature = "fdb-6_2",
244 feature = "fdb-6_3",
245 feature = "fdb-7_0",
246 feature = "fdb-7_1",
247 feature = "fdb-7_3"
248 );
249
250 let attrs = quote!(min = 500);
251
252 test_cfg_versions(expected_versions, attrs)
253 }
254
255 #[test]
256 #[should_panic]
257 fn test_no_min_version() {
258 let expected_versions = quote!(feature = "fdb-5_0",);
259
260 let attrs = quote!(max = 500);
261
262 test_cfg_versions(expected_versions, attrs)
263 }
264}