1//! Contains code for selecting features
2
3#![deny(unused_extern_crates)]
4#![deny(clippy::missing_docs_in_private_items)]
5#![allow(deprecated)]
6
7use std::cmp::Ordering;
8use std::io;
9use std::str::FromStr;
10
11/// This macro defines the [`RustTarget`] and [`RustFeatures`] types.
12macro_rules! define_rust_targets {
13 (
14 Nightly => {$($nightly_feature:ident $(: #$issue:literal)?),* $(,)?} $(,)?
15 $(
16 $(#[$attrs:meta])*
17 $variant:ident($minor:literal) => {$($feature:ident $(: #$pull:literal)?),* $(,)?},
18 )*
19 $(,)?
20 ) => {
21 /// Represents the version of the Rust language to target.
22 ///
23 /// To support a beta release, use the corresponding stable release.
24 ///
25 /// This enum will have more variants added as necessary.
26 #[allow(non_camel_case_types)]
27 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
28 pub enum RustTarget {
29 /// Rust Nightly
30 $(#[doc = concat!(
31 "- [`", stringify!($nightly_feature), "`]",
32 "(", $("https://github.com/rust-lang/rust/pull/", stringify!($issue),)* ")",
33 )])*
34 Nightly,
35 $(
36 #[doc = concat!("Rust 1.", stringify!($minor))]
37 $(#[doc = concat!(
38 "- [`", stringify!($feature), "`]",
39 "(", $("https://github.com/rust-lang/rust/pull/", stringify!($pull),)* ")",
40 )])*
41 $(#[$attrs])*
42 $variant,
43 )*
44 }
45
46 impl RustTarget {
47 fn minor(self) -> Option<u64> {
48 match self {
49 $( Self::$variant => Some($minor),)*
50 Self::Nightly => None
51 }
52 }
53
54 const fn stable_releases() -> [(Self, u64); [$($minor,)*].len()] {
55 [$((Self::$variant, $minor),)*]
56 }
57 }
58
59 #[cfg(feature = "__cli")]
60 /// Strings of allowed `RustTarget` values
61 pub const RUST_TARGET_STRINGS: &[&str] = &[$(concat!("1.", stringify!($minor)),)*];
62
63 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
64 pub(crate) struct RustFeatures {
65 $($(pub(crate) $feature: bool,)*)*
66 $(pub(crate) $nightly_feature: bool,)*
67 }
68
69 impl From<RustTarget> for RustFeatures {
70 fn from(target: RustTarget) -> Self {
71 if target == RustTarget::Nightly {
72 return Self {
73 $($($feature: true,)*)*
74 $($nightly_feature: true,)*
75 };
76 }
77
78 let mut features = Self {
79 $($($feature: false,)*)*
80 $($nightly_feature: false,)*
81 };
82
83 $(if target >= RustTarget::$variant {
84 $(features.$feature = true;)*
85 })*
86
87 features
88 }
89 }
90 };
91}
92
93// NOTE: When adding or removing features here, make sure to add the stabilization PR
94// number for the feature if it has been stabilized or the tracking issue number if the feature is
95// not stable.
96define_rust_targets! {
97 Nightly => {
98 vectorcall_abi,
99 },
100 Stable_1_73(73) => { thiscall_abi: #42202 },
101 Stable_1_71(71) => { c_unwind_abi: #106075 },
102 Stable_1_68(68) => { abi_efiapi: #105795 },
103 Stable_1_64(64) => { core_ffi_c: #94503 },
104 Stable_1_59(59) => { const_cstr: #54745 },
105 Stable_1_47(47) => { larger_arrays: #74060 },
106 Stable_1_40(40) => { non_exhaustive: #44109 },
107 Stable_1_36(36) => { maybe_uninit: #60445 },
108 Stable_1_33(33) => { repr_packed_n: #57049 },
109 #[deprecated]
110 Stable_1_30(30) => {
111 core_ffi_c_void: #53910,
112 min_const_fn: #54835,
113 },
114 #[deprecated]
115 Stable_1_28(28) => { repr_transparent: #51562 },
116 #[deprecated]
117 Stable_1_27(27) => { must_use_function: #48925 },
118 #[deprecated]
119 Stable_1_26(26) => { i128_and_u128: #49101 },
120 #[deprecated]
121 Stable_1_25(25) => { repr_align: #47006 },
122 #[deprecated]
123 Stable_1_21(21) => { builtin_clone_impls: #43690 },
124 #[deprecated]
125 Stable_1_20(20) => { associated_const: #42809 },
126 #[deprecated]
127 Stable_1_19(19) => { untagged_union: #42068 },
128 #[deprecated]
129 Stable_1_17(17) => { static_lifetime_elision: #39265 },
130 #[deprecated]
131 Stable_1_0(0) => {},
132}
133
134/// Latest stable release of Rust
135pub const LATEST_STABLE_RUST: RustTarget = {
136 // FIXME: replace all this code by
137 // ```
138 // RustTarget::stable_releases()
139 // .into_iter()
140 // .max_by_key(|(_, m)| m)
141 // .map(|(t, _)| t)
142 // .unwrap_or(RustTarget::Nightly)
143 // ```
144 // once those operations can be used in constants.
145 let targets = RustTarget::stable_releases();
146
147 let mut i = 0;
148 let mut latest_target = None;
149 let mut latest_minor = 0;
150
151 while i < targets.len() {
152 let (target, minor) = targets[i];
153
154 if latest_minor < minor {
155 latest_minor = minor;
156 latest_target = Some(target);
157 }
158
159 i += 1;
160 }
161
162 match latest_target {
163 Some(target) => target,
164 None => unreachable!(),
165 }
166};
167
168impl Default for RustTarget {
169 fn default() -> Self {
170 LATEST_STABLE_RUST
171 }
172}
173
174impl PartialOrd for RustTarget {
175 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
176 Some(self.cmp(other))
177 }
178}
179
180impl Ord for RustTarget {
181 fn cmp(&self, other: &Self) -> Ordering {
182 match (self.minor(), other.minor()) {
183 (Some(a: u64), Some(b: u64)) => a.cmp(&b),
184 (Some(_), None) => Ordering::Less,
185 (None, Some(_)) => Ordering::Greater,
186 (None, None) => Ordering::Equal,
187 }
188 }
189}
190
191impl FromStr for RustTarget {
192 type Err = io::Error;
193
194 fn from_str(s: &str) -> Result<Self, Self::Err> {
195 if s == "nightly" {
196 return Ok(Self::Nightly);
197 }
198
199 if let Some(("1", str_minor: &str)) = s.split_once(delimiter:'.') {
200 if let Ok(minor: u64) = str_minor.parse::<u64>() {
201 for (target: RustTarget, target_minor: u64) in Self::stable_releases() {
202 if minor == target_minor {
203 return Ok(target);
204 }
205 }
206 }
207 }
208
209 Err(io::Error::new(
210 kind:io::ErrorKind::InvalidInput,
211 error:"Got an invalid Rust target. Accepted values are of the form \"1.71\" or \"nightly\"."
212 ))
213 }
214}
215
216impl std::fmt::Display for RustTarget {
217 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218 match self.minor() {
219 Some(minor: u64) => write!(f, "1.{}", minor),
220 None => "nightly".fmt(f),
221 }
222 }
223}
224
225impl Default for RustFeatures {
226 fn default() -> Self {
227 RustTarget::default().into()
228 }
229}
230
231#[cfg(test)]
232mod test {
233 #![allow(unused_imports)]
234 use super::*;
235
236 #[test]
237 fn target_features() {
238 let f_1_0 = RustFeatures::from(RustTarget::Stable_1_0);
239 assert!(
240 !f_1_0.static_lifetime_elision &&
241 !f_1_0.core_ffi_c_void &&
242 !f_1_0.untagged_union &&
243 !f_1_0.associated_const &&
244 !f_1_0.builtin_clone_impls &&
245 !f_1_0.repr_align &&
246 !f_1_0.thiscall_abi &&
247 !f_1_0.vectorcall_abi
248 );
249 let f_1_21 = RustFeatures::from(RustTarget::Stable_1_21);
250 assert!(
251 f_1_21.static_lifetime_elision &&
252 !f_1_21.core_ffi_c_void &&
253 f_1_21.untagged_union &&
254 f_1_21.associated_const &&
255 f_1_21.builtin_clone_impls &&
256 !f_1_21.repr_align &&
257 !f_1_21.thiscall_abi &&
258 !f_1_21.vectorcall_abi
259 );
260 let features = RustFeatures::from(RustTarget::Stable_1_71);
261 assert!(
262 features.c_unwind_abi &&
263 features.abi_efiapi &&
264 !features.thiscall_abi
265 );
266 let f_nightly = RustFeatures::from(RustTarget::Nightly);
267 assert!(
268 f_nightly.static_lifetime_elision &&
269 f_nightly.core_ffi_c_void &&
270 f_nightly.untagged_union &&
271 f_nightly.associated_const &&
272 f_nightly.builtin_clone_impls &&
273 f_nightly.maybe_uninit &&
274 f_nightly.repr_align &&
275 f_nightly.thiscall_abi &&
276 f_nightly.vectorcall_abi
277 );
278 }
279
280 fn test_target(target_str: &str, target: RustTarget) {
281 let target_string = target.to_string();
282 assert_eq!(target_str, target_string);
283 assert_eq!(target, RustTarget::from_str(target_str).unwrap());
284 }
285
286 #[test]
287 fn str_to_target() {
288 test_target("1.0", RustTarget::Stable_1_0);
289 test_target("1.17", RustTarget::Stable_1_17);
290 test_target("1.19", RustTarget::Stable_1_19);
291 test_target("1.21", RustTarget::Stable_1_21);
292 test_target("1.25", RustTarget::Stable_1_25);
293 test_target("1.71", RustTarget::Stable_1_71);
294 test_target("nightly", RustTarget::Nightly);
295 }
296}
297