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.33
106             => Stable_1_33 => 1.33;
107             /// Nightly rust
108             => Nightly => nightly;
109         );
110     }
111 }
112 
113 rust_target_base!(rust_target_def);
114 rust_target_base!(rust_target_values_def);
115 
116 /// Latest stable release of Rust
117 pub const LATEST_STABLE_RUST: RustTarget = RustTarget::Stable_1_33;
118 
119 /// Create RustFeatures struct definition, new(), and a getter for each field
120 macro_rules! rust_feature_def {
121     (
122         $( $rust_target:ident {
123             $( $( #[$attr:meta] )* => $feature:ident; )*
124         } )*
125     ) => {
126         /// Features supported by a rust target
127         #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
128         pub struct RustFeatures {
129             $( $(
130                 $(
131                     #[$attr]
132                 )*
133                 pub $feature: bool,
134             )* )*
135         }
136 
137         impl RustFeatures {
138             /// Gives a RustFeatures struct with all features disabled
139             fn new() -> Self {
140                 RustFeatures {
141                     $( $(
142                         $feature: false,
143                     )* )*
144                 }
145             }
146         }
147 
148         impl From<RustTarget> for RustFeatures {
149             fn from(rust_target: RustTarget) -> Self {
150                 let mut features = RustFeatures::new();
151 
152                 $(
153                 if rust_target >= RustTarget::$rust_target {
154                     $(
155                     features.$feature = true;
156                     )*
157                 }
158                 )*
159 
160                 features
161             }
162         }
163     }
164 }
165 
166 rust_feature_def!(
167     Stable_1_19 {
168         /// Untagged unions ([RFC 1444](https://github.com/rust-lang/rfcs/blob/master/text/1444-union.md))
169         => untagged_union;
170     }
171     Stable_1_20 {
172         /// associated constants ([PR](https://github.com/rust-lang/rust/pull/42809))
173         => associated_const;
174     }
175     Stable_1_21 {
176         /// builtin impls for `Clone` ([PR](https://github.com/rust-lang/rust/pull/43690))
177         => builtin_clone_impls;
178     }
179     Stable_1_25 {
180         /// repr(align) ([PR](https://github.com/rust-lang/rust/pull/47006))
181         => repr_align;
182     }
183     Stable_1_26 {
184         /// [i128 / u128 support](https://doc.rust-lang.org/std/primitive.i128.html)
185         => i128_and_u128;
186     }
187     Stable_1_27 {
188         /// `must_use` attribute on functions ([PR](https://github.com/rust-lang/rust/pull/48925))
189         => must_use_function;
190     }
191     Stable_1_28 {
192         /// repr(transparent) ([PR](https://github.com/rust-lang/rust/pull/51562))
193         => repr_transparent;
194     }
195     Stable_1_33 {
196         /// repr(packed(N)) ([PR](https://github.com/rust-lang/rust/pull/57049))
197         => repr_packed_n;
198     }
199     Nightly {
200         /// `thiscall` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/42202))
201         => thiscall_abi;
202     }
203 );
204 
205 impl Default for RustFeatures {
default() -> Self206     fn default() -> Self {
207         let default_rust_target: RustTarget = Default::default();
208         Self::from(default_rust_target)
209     }
210 }
211 
212 #[cfg(test)]
213 mod test {
214     #![allow(unused_imports)]
215     use super::*;
216 
217     #[test]
target_features()218     fn target_features() {
219         let f_1_0 = RustFeatures::from(RustTarget::Stable_1_0);
220         assert!(
221             !f_1_0.untagged_union
222                 && !f_1_0.associated_const
223                 && !f_1_0.builtin_clone_impls
224                 && !f_1_0.repr_align
225                 && !f_1_0.thiscall_abi
226         );
227         let f_1_21 = RustFeatures::from(RustTarget::Stable_1_21);
228         assert!(
229             f_1_21.untagged_union
230                 && f_1_21.associated_const
231                 && f_1_21.builtin_clone_impls
232                 && !f_1_21.repr_align
233                 && !f_1_21.thiscall_abi
234         );
235         let f_nightly = RustFeatures::from(RustTarget::Nightly);
236         assert!(
237             f_nightly.untagged_union
238                 && f_nightly.associated_const
239                 && f_nightly.builtin_clone_impls
240                 && f_nightly.repr_align
241                 && f_nightly.thiscall_abi
242         );
243     }
244 
test_target(target_str: &str, target: RustTarget)245     fn test_target(target_str: &str, target: RustTarget) {
246         let target_string: String = target.into();
247         assert_eq!(target_str, target_string);
248         assert_eq!(target, RustTarget::from_str(target_str).unwrap());
249     }
250 
251     #[test]
str_to_target()252     fn str_to_target() {
253         test_target("1.0", RustTarget::Stable_1_0);
254         test_target("1.19", RustTarget::Stable_1_19);
255         test_target("1.21", RustTarget::Stable_1_21);
256         test_target("1.25", RustTarget::Stable_1_25);
257         test_target("nightly", RustTarget::Nightly);
258     }
259 }
260