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