| //! Contains code for selecting features |
| |
| #![deny(missing_docs)] |
| #![deny(warnings)] |
| #![deny(unused_extern_crates)] |
| |
| use std::io; |
| use std::str::FromStr; |
| |
| /// Define RustTarget struct definition, Default impl, and conversions |
| /// between RustTarget and String. |
| macro_rules! rust_target_def { |
| ( $( $( #[$attr:meta] )* => $release:ident => $value:expr; )* ) => { |
| /// Represents the version of the Rust language to target. |
| /// |
| /// To support a beta release, use the corresponding stable release. |
| /// |
| /// This enum will have more variants added as necessary. |
| #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Hash)] |
| #[allow(non_camel_case_types)] |
| pub enum RustTarget { |
| $( |
| $( |
| #[$attr] |
| )* |
| $release, |
| )* |
| } |
| |
| impl Default for RustTarget { |
| /// Gives the latest stable Rust version |
| fn default() -> RustTarget { |
| LATEST_STABLE_RUST |
| } |
| } |
| |
| impl FromStr for RustTarget { |
| type Err = io::Error; |
| |
| /// Create a `RustTarget` from a string. |
| /// |
| /// * The stable/beta versions of Rust are of the form "1.0", |
| /// "1.19", etc. |
| /// * The nightly version should be specified with "nightly". |
| fn from_str(s: &str) -> Result<Self, Self::Err> { |
| match s.as_ref() { |
| $( |
| stringify!($value) => Ok(RustTarget::$release), |
| )* |
| _ => Err( |
| io::Error::new( |
| io::ErrorKind::InvalidInput, |
| concat!( |
| "Got an invalid rust target. Accepted values ", |
| "are of the form ", |
| "\"1.0\" or \"nightly\"."))), |
| } |
| } |
| } |
| |
| impl From<RustTarget> for String { |
| fn from(target: RustTarget) -> Self { |
| match target { |
| $( |
| RustTarget::$release => stringify!($value), |
| )* |
| }.into() |
| } |
| } |
| } |
| } |
| |
| /// Defines an array slice with all RustTarget values |
| macro_rules! rust_target_values_def { |
| ( $( $( #[$attr:meta] )* => $release:ident => $value:expr; )* ) => { |
| /// Strings of allowed `RustTarget` values |
| pub static RUST_TARGET_STRINGS: &'static [&str] = &[ |
| $( |
| stringify!($value), |
| )* |
| ]; |
| } |
| } |
| |
| /// Defines macro which takes a macro |
| macro_rules! rust_target_base { |
| ( $x_macro:ident ) => { |
| $x_macro!( |
| /// Rust stable 1.0 |
| => Stable_1_0 => 1.0; |
| /// Rust stable 1.19 |
| => Stable_1_19 => 1.19; |
| /// Rust stable 1.20 |
| => Stable_1_20 => 1.20; |
| /// Rust stable 1.21 |
| => Stable_1_21 => 1.21; |
| /// Rust stable 1.25 |
| => Stable_1_25 => 1.25; |
| /// Rust stable 1.26 |
| => Stable_1_26 => 1.26; |
| /// Rust stable 1.27 |
| => Stable_1_27 => 1.27; |
| /// Rust stable 1.28 |
| => Stable_1_28 => 1.28; |
| /// Rust stable 1.30 |
| => Stable_1_30 => 1.30; |
| /// Rust stable 1.33 |
| => Stable_1_33 => 1.33; |
| /// Nightly rust |
| => Nightly => nightly; |
| ); |
| } |
| } |
| |
| rust_target_base!(rust_target_def); |
| rust_target_base!(rust_target_values_def); |
| |
| /// Latest stable release of Rust |
| pub const LATEST_STABLE_RUST: RustTarget = RustTarget::Stable_1_33; |
| |
| /// Create RustFeatures struct definition, new(), and a getter for each field |
| macro_rules! rust_feature_def { |
| ( |
| $( $rust_target:ident { |
| $( $( #[$attr:meta] )* => $feature:ident; )* |
| } )* |
| ) => { |
| /// Features supported by a rust target |
| #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] |
| pub struct RustFeatures { |
| $( $( |
| $( |
| #[$attr] |
| )* |
| pub $feature: bool, |
| )* )* |
| } |
| |
| impl RustFeatures { |
| /// Gives a RustFeatures struct with all features disabled |
| fn new() -> Self { |
| RustFeatures { |
| $( $( |
| $feature: false, |
| )* )* |
| } |
| } |
| } |
| |
| impl From<RustTarget> for RustFeatures { |
| fn from(rust_target: RustTarget) -> Self { |
| let mut features = RustFeatures::new(); |
| |
| $( |
| if rust_target >= RustTarget::$rust_target { |
| $( |
| features.$feature = true; |
| )* |
| } |
| )* |
| |
| features |
| } |
| } |
| } |
| } |
| |
| rust_feature_def!( |
| Stable_1_19 { |
| /// Untagged unions ([RFC 1444](https://github.com/rust-lang/rfcs/blob/master/text/1444-union.md)) |
| => untagged_union; |
| } |
| Stable_1_20 { |
| /// associated constants ([PR](https://github.com/rust-lang/rust/pull/42809)) |
| => associated_const; |
| } |
| Stable_1_21 { |
| /// builtin impls for `Clone` ([PR](https://github.com/rust-lang/rust/pull/43690)) |
| => builtin_clone_impls; |
| } |
| Stable_1_25 { |
| /// repr(align) ([PR](https://github.com/rust-lang/rust/pull/47006)) |
| => repr_align; |
| } |
| Stable_1_26 { |
| /// [i128 / u128 support](https://doc.rust-lang.org/std/primitive.i128.html) |
| => i128_and_u128; |
| } |
| Stable_1_27 { |
| /// `must_use` attribute on functions ([PR](https://github.com/rust-lang/rust/pull/48925)) |
| => must_use_function; |
| } |
| Stable_1_28 { |
| /// repr(transparent) ([PR](https://github.com/rust-lang/rust/pull/51562)) |
| => repr_transparent; |
| } |
| Stable_1_30 { |
| /// `const fn` support for limited cases |
| /// ([PR](https://github.com/rust-lang/rust/pull/54835/) |
| => min_const_fn; |
| } |
| Stable_1_33 { |
| /// repr(packed(N)) ([PR](https://github.com/rust-lang/rust/pull/57049)) |
| => repr_packed_n; |
| } |
| Nightly { |
| /// `thiscall` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/42202)) |
| => thiscall_abi; |
| /// `non_exhaustive` enums/structs ([Tracking issue](https://github.com/rust-lang/rust/issues/44109)) |
| => non_exhaustive; |
| } |
| ); |
| |
| impl Default for RustFeatures { |
| fn default() -> Self { |
| let default_rust_target: RustTarget = Default::default(); |
| Self::from(default_rust_target) |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| #![allow(unused_imports)] |
| use super::*; |
| |
| #[test] |
| fn target_features() { |
| let f_1_0 = RustFeatures::from(RustTarget::Stable_1_0); |
| assert!( |
| !f_1_0.untagged_union && |
| !f_1_0.associated_const && |
| !f_1_0.builtin_clone_impls && |
| !f_1_0.repr_align && |
| !f_1_0.thiscall_abi |
| ); |
| let f_1_21 = RustFeatures::from(RustTarget::Stable_1_21); |
| assert!( |
| f_1_21.untagged_union && |
| f_1_21.associated_const && |
| f_1_21.builtin_clone_impls && |
| !f_1_21.repr_align && |
| !f_1_21.thiscall_abi |
| ); |
| let f_nightly = RustFeatures::from(RustTarget::Nightly); |
| assert!( |
| f_nightly.untagged_union && |
| f_nightly.associated_const && |
| f_nightly.builtin_clone_impls && |
| f_nightly.repr_align && |
| f_nightly.thiscall_abi |
| ); |
| } |
| |
| fn test_target(target_str: &str, target: RustTarget) { |
| let target_string: String = target.into(); |
| assert_eq!(target_str, target_string); |
| assert_eq!(target, RustTarget::from_str(target_str).unwrap()); |
| } |
| |
| #[test] |
| fn str_to_target() { |
| test_target("1.0", RustTarget::Stable_1_0); |
| test_target("1.19", RustTarget::Stable_1_19); |
| test_target("1.21", RustTarget::Stable_1_21); |
| test_target("1.25", RustTarget::Stable_1_25); |
| test_target("nightly", RustTarget::Nightly); |
| } |
| } |