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, 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_4".into(), 740);
97 version_mapping.insert("fdb-7_3".into(), 730);
98 version_mapping.insert("fdb-7_1".into(), 710);
99 version_mapping.insert("fdb-7_0".into(), 700);
100 version_mapping.insert("fdb-6_3".into(), 630);
101 version_mapping.insert("fdb-6_2".into(), 620);
102 version_mapping.insert("fdb-6_1".into(), 610);
103 version_mapping.insert("fdb-6_0".into(), 600);
104 version_mapping.insert("fdb-5_2".into(), 520);
105 version_mapping.insert("fdb-5_1".into(), 510);
106 version_mapping.insert("fdb-5_0".into(), 500);
107 version_mapping
108}
109
110#[cfg(test)]
111mod tests {
112 use crate::cfg_api_versions_impl;
113 use crate::get_supported_feature_range;
114 use proc_macro2::TokenStream;
115 use quote::quote;
116
117 #[test]
118 fn test_create_supported_list() {
119 let v = get_supported_feature_range(700, None);
120 assert_eq!(v.len(), 4);
121 assert!(v.contains(&String::from("fdb-7_0")));
122 assert!(v.contains(&String::from("fdb-7_1")));
123 assert!(v.contains(&String::from("fdb-7_3")));
124 assert!(v.contains(&String::from("fdb-7_4")));
125
126 let v = get_supported_feature_range(600, Some(700));
127 assert_eq!(v.len(), 5);
128 assert!(v.contains(&String::from("fdb-7_0")));
129 assert!(v.contains(&String::from("fdb-6_3")));
130 assert!(v.contains(&String::from("fdb-6_2")));
131 assert!(v.contains(&String::from("fdb-6_1")));
132 assert!(v.contains(&String::from("fdb-6_0")));
133
134 let v = get_supported_feature_range(500, Some(610));
135 assert_eq!(v.len(), 5);
136 assert!(v.contains(&String::from("fdb-6_1")));
137 assert!(v.contains(&String::from("fdb-6_0")));
138 assert!(v.contains(&String::from("fdb-5_2")));
139 assert!(v.contains(&String::from("fdb-5_1")));
140 assert!(v.contains(&String::from("fdb-5_0")));
141
142 let v = get_supported_feature_range(500, None);
143 assert_eq!(v.len(), 11);
144 assert!(v.contains(&String::from("fdb-7_4")));
145 assert!(v.contains(&String::from("fdb-7_3")));
146 assert!(v.contains(&String::from("fdb-7_1")));
147 assert!(v.contains(&String::from("fdb-7_0")));
148 assert!(v.contains(&String::from("fdb-6_3")));
149 assert!(v.contains(&String::from("fdb-6_2")));
150 assert!(v.contains(&String::from("fdb-6_1")));
151 assert!(v.contains(&String::from("fdb-6_0")));
152 assert!(v.contains(&String::from("fdb-5_2")));
153 assert!(v.contains(&String::from("fdb-5_1")));
154 assert!(v.contains(&String::from("fdb-5_0")));
155 }
156
157 fn test_cfg_versions(expected_versions: TokenStream, attrs: TokenStream) {
158 let input = quote! {
159 fn ma_fonction() {}
160 };
161
162 let expected = quote! {
163 #[cfg(any(#expected_versions))]
164 fn ma_fonction() {}
165 };
166
167 let result = cfg_api_versions_impl(attrs, input);
168 assert_eq!(result.to_string(), expected.to_string())
169 }
170
171 #[test]
172 fn test_min_700_no_max_version() {
173 let data = quote!(
174 feature = "fdb-7_0",
175 feature = "fdb-7_1",
176 feature = "fdb-7_3"
177 feature = "fdb-7_4"
178 );
179
180 let attrs = quote!(min = 700);
181
182 test_cfg_versions(data, attrs)
183 }
184
185 #[test]
186 fn test_min_600_max_700() {
187 let expected_versions = quote!(
188 feature = "fdb-6_0",
189 feature = "fdb-6_1",
190 feature = "fdb-6_2",
191 feature = "fdb-6_3",
192 feature = "fdb-7_0"
193 );
194
195 let attrs = quote!(min = 600, max = 700);
196
197 test_cfg_versions(expected_versions, attrs)
198 }
199
200 #[test]
201 fn test_min_500_max_610() {
202 let expected_versions = quote!(
203 feature = "fdb-5_0",
204 feature = "fdb-5_1",
205 feature = "fdb-5_2",
206 feature = "fdb-6_0",
207 feature = "fdb-6_1"
208 );
209
210 let attrs = quote!(min = 500, max = 610);
211
212 test_cfg_versions(expected_versions, attrs)
213 }
214
215 #[test]
216 fn test_min_500_no_max() {
217 let expected_versions = quote!(
218 feature = "fdb-5_0",
219 feature = "fdb-5_1",
220 feature = "fdb-5_2",
221 feature = "fdb-6_0",
222 feature = "fdb-6_1",
223 feature = "fdb-6_2",
224 feature = "fdb-6_3",
225 feature = "fdb-7_0",
226 feature = "fdb-7_1",
227 feature = "fdb-7_3"
228 feature = "fdb-7_4"
229 );
230
231 let attrs = quote!(min = 500);
232
233 test_cfg_versions(expected_versions, attrs)
234 }
235
236 #[test]
237 #[should_panic]
238 fn test_no_min_version() {
239 let expected_versions = quote!(feature = "fdb-5_0",);
240
241 let attrs = quote!(max = 500);
242
243 test_cfg_versions(expected_versions, attrs)
244 }
245}