diff options
Diffstat (limited to 'src/features.rs')
-rw-r--r-- | src/features.rs | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/features.rs b/src/features.rs new file mode 100644 index 00000000..3f59c6b9 --- /dev/null +++ b/src/features.rs @@ -0,0 +1,185 @@ +//! 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; + /// 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_19; + +/// Create RustFeatures struct definition, new(), and a getter for each field +macro_rules! rust_feature_def { + ( $( $( #[$attr:meta] )* => $feature:ident; )* ) => { + /// Features supported by a rust target + #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] + pub struct RustFeatures { + $( + $feature: bool, + )* + } + + impl RustFeatures { + /// Gives a RustFeatures struct with all features disabled + fn new() -> Self { + RustFeatures { + $( + $feature: false, + )* + } + } + + $( + $( + #[$attr] + )* + pub fn $feature(&self) -> bool { + self.$feature + } + )* + } + } +} + +rust_feature_def!( + /// Untagged unions ([RFC 1444](https://github.com/rust-lang/rfcs/blob/master/text/1444-union.md)) + => untagged_union; + /// Constant function ([RFC 911](https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md)) + => const_fn; +); + +impl From<RustTarget> for RustFeatures { + fn from(rust_target: RustTarget) -> Self { + let mut features = RustFeatures::new(); + + if rust_target >= RustTarget::Stable_1_19 { + features.untagged_union = true; + } + + if rust_target >= RustTarget::Nightly { + features.const_fn = true; + } + + features + } +} + +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::*; + + 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("nightly", RustTarget::Nightly); + } +} |