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