|
| 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 | + #[allow(dead_code)] |
| 41 | + /// Create a `RustTarget` from a string. |
| 42 | + /// |
| 43 | + /// * The stable/beta versions of Rust are of the form "1.0", |
| 44 | + /// "1.19", etc. |
| 45 | + /// * The nightly version should be specified with "nightly". |
| 46 | + fn from_str(s: &str) -> Result<Self, Self::Err> { |
| 47 | + match s.as_ref() { |
| 48 | + $( |
| 49 | + stringify!($value) => Ok(RustTarget::$release), |
| 50 | + )* |
| 51 | + _ => Err( |
| 52 | + io::Error::new( |
| 53 | + io::ErrorKind::InvalidInput, |
| 54 | + concat!( |
| 55 | + "Got an invalid rust target. Accepted values ", |
| 56 | + "are of the form ", |
| 57 | + "\"1.0\" or \"nightly\"."))), |
| 58 | + } |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + impl From<RustTarget> for String { |
| 63 | + fn from(target: RustTarget) -> Self { |
| 64 | + match target { |
| 65 | + $( |
| 66 | + RustTarget::$release => stringify!($value), |
| 67 | + )* |
| 68 | + }.into() |
| 69 | + } |
| 70 | + } |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +/// Defines an array slice with all RustTarget values |
| 75 | +macro_rules! rust_target_values_def { |
| 76 | + ( $( $( #[$attr:meta] )* => $release:ident => $value:expr; )* ) => { |
| 77 | + /// Strings of allowed `RustTarget` values |
| 78 | + #[allow(dead_code)] |
| 79 | + pub static RUST_TARGET_STRINGS: &'static [&str] = &[ |
| 80 | + $( |
| 81 | + stringify!($value), |
| 82 | + )* |
| 83 | + ]; |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +/// Defines macro which takes a macro |
| 88 | +macro_rules! rust_target_base { |
| 89 | + ( $x_macro:ident ) => { |
| 90 | + $x_macro!( |
| 91 | + /// Rust stable 1.0 |
| 92 | + => Stable_1_0 => 1.0; |
| 93 | + /// Rust stable 1.19 |
| 94 | + => Stable_1_19 => 1.19; |
| 95 | + /// Nightly rust |
| 96 | + => Nightly => nightly; |
| 97 | + ); |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +rust_target_base!(rust_target_def); |
| 102 | +rust_target_base!(rust_target_values_def); |
| 103 | + |
| 104 | +/// Latest stable release of Rust |
| 105 | +pub const LATEST_STABLE_RUST: RustTarget = RustTarget::Stable_1_19; |
| 106 | + |
| 107 | +/// Create RustFeatures struct definition, new(), and a getter for each field |
| 108 | +macro_rules! rust_feature_def { |
| 109 | + ( $( $( #[$attr:meta] )* => $feature:ident; )* ) => { |
| 110 | + /// Features supported by a rust target |
| 111 | + #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] |
| 112 | + pub struct RustFeatures { |
| 113 | + $( |
| 114 | + $feature: bool, |
| 115 | + )* |
| 116 | + } |
| 117 | + |
| 118 | + impl RustFeatures { |
| 119 | + /// Gives a RustFeatures struct with all features disabled |
| 120 | + fn new() -> Self { |
| 121 | + RustFeatures { |
| 122 | + $( |
| 123 | + $feature: false, |
| 124 | + )* |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + $( |
| 129 | + $( |
| 130 | + #[$attr] |
| 131 | + )* |
| 132 | + pub fn $feature(&self) -> bool { |
| 133 | + self.$feature |
| 134 | + } |
| 135 | + )* |
| 136 | + } |
| 137 | + } |
| 138 | +} |
| 139 | + |
| 140 | +rust_feature_def!( |
| 141 | + /// Untagged unions ([RFC 1444](https://github.com/rust-lang/rfcs/blob/master/text/1444-union.md)) |
| 142 | + => untagged_union; |
| 143 | + /// Constant function ([RFC 911](https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md)) |
| 144 | + => const_fn; |
| 145 | +); |
| 146 | + |
| 147 | +impl From<RustTarget> for RustFeatures { |
| 148 | + fn from(rust_target: RustTarget) -> Self { |
| 149 | + let mut features = RustFeatures::new(); |
| 150 | + |
| 151 | + if rust_target >= RustTarget::Stable_1_19 { |
| 152 | + features.untagged_union = true; |
| 153 | + } |
| 154 | + |
| 155 | + if rust_target >= RustTarget::Nightly { |
| 156 | + features.const_fn = true; |
| 157 | + } |
| 158 | + |
| 159 | + features |
| 160 | + } |
| 161 | +} |
| 162 | + |
| 163 | +impl Default for RustFeatures { |
| 164 | + fn default() -> Self { |
| 165 | + let default_rust_target: RustTarget = Default::default(); |
| 166 | + Self::from(default_rust_target) |
| 167 | + } |
| 168 | +} |
| 169 | + |
| 170 | +#[cfg(test)] |
| 171 | +mod test { |
| 172 | + #![allow(unused_imports)] |
| 173 | + use super::*; |
| 174 | + |
| 175 | + fn test_target(target_str: &str, target: RustTarget) { |
| 176 | + let target_string: String = target.into(); |
| 177 | + assert_eq!(target_str, target_string); |
| 178 | + assert_eq!(target, RustTarget::from_str(target_str).unwrap()); |
| 179 | + } |
| 180 | + |
| 181 | + #[test] |
| 182 | + fn str_to_target() { |
| 183 | + test_target("1.0", RustTarget::Stable_1_0); |
| 184 | + test_target("1.19", RustTarget::Stable_1_19); |
| 185 | + test_target("nightly", RustTarget::Nightly); |
| 186 | + } |
| 187 | +} |
0 commit comments