1 //! Contains code for selecting features
2 
3 #![deny(missing_docs)]
4 #![deny(warnings)]
5 #![deny(unused_extern_crates)]
6 
7 use std::io;
8 use std::str::FromStr;
9 
10 /// Define RustTarget struct definition, Default impl, and conversions
11 /// between RustTarget and String.
12 macro_rules! rust_target_def {
13     ( $( $( #[$attr:meta] )* => $release:ident => $value:expr; )* ) => {
14         /// Represents the version of the Rust language to target.
15         ///
16         /// To support a beta release, use the corresponding stable release.
17         ///
18         /// This enum will have more variants added as necessary.
19         #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Hash)]
20         #[allow(non_camel_case_types)]
21         pub enum RustTarget {
22             $(
23                 $(
24                     #[$attr]
25                 )*
26                 $release,
27             )*
28         }
29 
30         impl Default for RustTarget {
31             /// Gives the latest stable Rust version
32             fn default() -> RustTarget {
33                 LATEST_STABLE_RUST
34             }
35         }
36 
37         impl FromStr for RustTarget {
38             type Err = io::Error;
39 
40             /// Create a `RustTarget` from a string.
41             ///
42             /// * The stable/beta versions of Rust are of the form "1.0",
43             /// "1.19", etc.
44             /// * The nightly version should be specified with "nightly".
45             fn from_str(s: &str) -> Result<Self, Self::Err> {
46                 match s.as_ref() {
47                     $(
48                         stringify!($value) => Ok(RustTarget::$release),
49                     )*
50                     _ => Err(
51                         io::Error::new(
52                             io::ErrorKind::InvalidInput,
53                             concat!(
54                                 "Got an invalid rust target. Accepted values ",
55                                 "are of the form ",
56                                 "\"1.0\" or \"nightly\"."))),
57                 }
58             }
59         }
60 
61         impl From<RustTarget> for String {
62             fn from(target: RustTarget) -> Self {
63                 match target {
64                     $(
65                         RustTarget::$release => stringify!($value),
66                     )*
67                 }.into()
68             }
69         }
70     }
71 }
72 
73 /// Defines an array slice with all RustTarget values
74 macro_rules! rust_target_values_def {
75     ( $( $( #[$attr:meta] )* => $release:ident => $value:expr; )* ) => {
76         /// Strings of allowed `RustTarget` values
77         pub static RUST_TARGET_STRINGS: &'static [&str] = &[
78             $(
79                 stringify!($value),
80             )*
81         ];
82     }
83 }
84 
85 /// Defines macro which takes a macro
86 macro_rules! rust_target_base {
87     ( $x_macro:ident ) => {
88         $x_macro!(
89             /// Rust stable 1.0
90             => Stable_1_0 => 1.0;
91             /// Rust stable 1.19
92             => Stable_1_19 => 1.19;
93             /// Rust stable 1.20
94             => Stable_1_20 => 1.20;
95             /// Rust stable 1.21
96             => Stable_1_21 => 1.21;
97             /// Rust stable 1.25
98             => Stable_1_25 => 1.25;
99             /// Rust stable 1.26
100             => Stable_1_26 => 1.26;
101             /// Rust stable 1.27
102             => Stable_1_27 => 1.27;
103             /// Rust stable 1.28
104             => Stable_1_28 => 1.28;
105             /// Rust stable 1.30
106             => Stable_1_30 => 1.30;
107             /// Rust stable 1.33
108             => Stable_1_33 => 1.33;
109             /// Nightly rust
110             => Nightly => nightly;
111         );
112     }
113 }
114 
115 rust_target_base!(rust_target_def);
116 rust_target_base!(rust_target_values_def);
117 
118 /// Latest stable release of Rust
119 pub const LATEST_STABLE_RUST: RustTarget = RustTarget::Stable_1_33;
120 
121 /// Create RustFeatures struct definition, new(), and a getter for each field
122 macro_rules! rust_feature_def {
123     (
124         $( $rust_target:ident {
125             $( $( #[$attr:meta] )* => $feature:ident; )*
126         } )*
127     ) => {
128         /// Features supported by a rust target
129         #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
130         pub struct RustFeatures {
131             $( $(
132                 $(
133                     #[$attr]
134                 )*
135                 pub $feature: bool,
136             )* )*
137         }
138 
139         impl RustFeatures {
140             /// Gives a RustFeatures struct with all features disabled
141             fn new() -> Self {
142                 RustFeatures {
143                     $( $(
144                         $feature: false,
145                     )* )*
146                 }
147             }
148         }
149 
150         impl From<RustTarget> for RustFeatures {
151             fn from(rust_target: RustTarget) -> Self {
152                 let mut features = RustFeatures::new();
153 
154                 $(
155                 if rust_target >= RustTarget::$rust_target {
156                     $(
157                     features.$feature = true;
158                     )*
159                 }
160                 )*
161 
162                 features
163             }
164         }
165     }
166 }
167 
168 rust_feature_def!(
169     Stable_1_19 {
170         /// Untagged unions ([RFC 1444](https://github.com/rust-lang/rfcs/blob/master/text/1444-union.md))
171         => untagged_union;
172     }
173     Stable_1_20 {
174         /// associated constants ([PR](https://github.com/rust-lang/rust/pull/42809))
175         => associated_const;
176     }
177     Stable_1_21 {
178         /// builtin impls for `Clone` ([PR](https://github.com/rust-lang/rust/pull/43690))
179         => builtin_clone_impls;
180     }
181     Stable_1_25 {
182         /// repr(align) ([PR](https://github.com/rust-lang/rust/pull/47006))
183         => repr_align;
184     }
185     Stable_1_26 {
186         /// [i128 / u128 support](https://doc.rust-lang.org/std/primitive.i128.html)
187         => i128_and_u128;
188     }
189     Stable_1_27 {
190         /// `must_use` attribute on functions ([PR](https://github.com/rust-lang/rust/pull/48925))
191         => must_use_function;
192     }
193     Stable_1_28 {
194         /// repr(transparent) ([PR](https://github.com/rust-lang/rust/pull/51562))
195         => repr_transparent;
196     }
197     Stable_1_30 {
198         /// `const fn` support for limited cases
199         /// ([PR](https://github.com/rust-lang/rust/pull/54835/)
200         => min_const_fn;
201     }
202     Stable_1_33 {
203         /// repr(packed(N)) ([PR](https://github.com/rust-lang/rust/pull/57049))
204         => repr_packed_n;
205     }
206     Nightly {
207         /// `thiscall` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/42202))
208         => thiscall_abi;
209         /// `non_exhaustive` enums/structs ([Tracking issue](https://github.com/rust-lang/rust/issues/44109))
210         => non_exhaustive;
211     }
212 );
213 
214 impl Default for RustFeatures {
default() -> Self215     fn default() -> Self {
216         let default_rust_target: RustTarget = Default::default();
217         Self::from(default_rust_target)
218     }
219 }
220 
221 #[cfg(test)]
222 mod test {
223     #![allow(unused_imports)]
224     use super::*;
225 
226     #[test]
target_features()227     fn target_features() {
228         let f_1_0 = RustFeatures::from(RustTarget::Stable_1_0);
229         assert!(
230             !f_1_0.untagged_union &&
231                 !f_1_0.associated_const &&
232                 !f_1_0.builtin_clone_impls &&
233                 !f_1_0.repr_align &&
234                 !f_1_0.thiscall_abi
235         );
236         let f_1_21 = RustFeatures::from(RustTarget::Stable_1_21);
237         assert!(
238             f_1_21.untagged_union &&
239                 f_1_21.associated_const &&
240                 f_1_21.builtin_clone_impls &&
241                 !f_1_21.repr_align &&
242                 !f_1_21.thiscall_abi
243         );
244         let f_nightly = RustFeatures::from(RustTarget::Nightly);
245         assert!(
246             f_nightly.untagged_union &&
247                 f_nightly.associated_const &&
248                 f_nightly.builtin_clone_impls &&
249                 f_nightly.repr_align &&
250                 f_nightly.thiscall_abi
251         );
252     }
253 
test_target(target_str: &str, target: RustTarget)254     fn test_target(target_str: &str, target: RustTarget) {
255         let target_string: String = target.into();
256         assert_eq!(target_str, target_string);
257         assert_eq!(target, RustTarget::from_str(target_str).unwrap());
258     }
259 
260     #[test]
str_to_target()261     fn str_to_target() {
262         test_target("1.0", RustTarget::Stable_1_0);
263         test_target("1.19", RustTarget::Stable_1_19);
264         test_target("1.21", RustTarget::Stable_1_21);
265         test_target("1.25", RustTarget::Stable_1_25);
266         test_target("nightly", RustTarget::Nightly);
267     }
268 }
269