build_cfg_shared 1.1.0

Shared structs and functions for build_cfg
Documentation
#[macro_use]
extern crate derive_more;

pub trait CfgTest {
	fn test(&self, val: &'static str) -> bool;
	fn put(&mut self, val: String);
}

#[derive(Debug, Default, Deref, DerefMut)]
pub struct CfgSet(std::collections::BTreeSet<String>);
impl CfgTest for CfgSet {
	#[inline]
	fn test(&self, val: &'static str) -> bool {
		self.contains(val)
	}

	#[inline]
	fn put(&mut self, val: String) {
		for val in val.split(",") {
			self.insert(val.to_string());
		}
	}
}

#[derive(Debug, Default, Deref, DerefMut)]
pub struct CfgScalar(String);
impl CfgTest for CfgScalar {
	#[inline]
	fn test(&self, val: &'static str) -> bool {
		self.as_str() == val
	}

	#[inline]
	fn put(&mut self, val: String) {
		self.0 = val;
	}
}
impl CfgTest for Option<CfgScalar> {
	#[inline]
	fn test(&self, val: &'static str) -> bool {
		if let Some(ref _self) = self {
			_self.test(val)
		} else {
			false
		}
	}

	#[inline]
	fn put(&mut self, val: String) {
		self.replace(CfgScalar(val));
	}
}

#[derive(Debug, Default)]
pub struct EnvCfg {
	pub target_os: CfgScalar,
	pub target_arch: CfgScalar,
	pub target_endian: CfgScalar,
	pub target_pointer_width: CfgScalar,
	pub target_family: Option<CfgScalar>,
	pub target_env: CfgScalar,
	pub target_vendor: Option<CfgScalar>,
	pub target_has_atomic: CfgSet,
	pub target_feature: CfgSet,
	pub feature: CfgSet
}

macro_rules! cfg_key {
	{ $($variant:ident = $val:ident),* } => {
		#[derive(Clone, Copy, Debug, PartialEq, Eq)]
		pub enum CfgKey {
			$($variant),*
		}
		impl CfgKey {
			pub fn lookup(self, cfg: &EnvCfg) -> &dyn CfgTest {
				match self {
					$(CfgKey::$variant => &cfg.$val),*
				}
			}

			pub fn lookup_mut(self, cfg: &mut EnvCfg) -> &mut dyn CfgTest {
				match self {
					$(CfgKey::$variant => &mut cfg.$val),*
				}
			}

			pub fn from_str<S: AsRef<str>>(str: S) -> Option<CfgKey> {
				match str.as_ref() {
					$(stringify!($val) => Some(CfgKey::$variant)),*,
					_ => None
				}
			}
		}
	};
}
cfg_key! {
	TargetOs = target_os,
	TargetArch = target_arch,
	TargetEndian = target_endian,
	TargetPointerWidth = target_pointer_width,
	TargetFamily = target_family,
	TargetEnv = target_env,
	TargetVendor = target_vendor,
	TargetHasAtomic = target_has_atomic,
	TargetFeature = target_feature,
	Feature = feature
}

#[derive(Clone, Copy, Debug)]
pub struct CfgDirective(pub CfgKey, pub &'static str);

#[derive(Debug)]
pub enum CfgPredicate {
	Any(Vec<CfgPredicate>),
	All(Vec<CfgPredicate>),
	Not(Box<CfgPredicate>),
	Directive(CfgDirective),
}
impl CfgPredicate {
	pub fn test(self) -> bool {
		match self {
			CfgPredicate::Any(directives) => {
				for predicate in directives {
					if predicate.test() {
						return true;
					}
				}
				false
			}
			CfgPredicate::All(directives) => {
				for predicate in directives {
					if !predicate.test() {
						return false;
					}
				}
				true
			}
			CfgPredicate::Not(predicate) => !predicate.test(),
			CfgPredicate::Directive(directive) => env_cfg(move |env_cfg| directive.0.lookup(env_cfg).test(directive.1)),
		}
	}
}

thread_local! {
	pub static __TARGET_CFG: std::cell::RefCell<Option<EnvCfg>> = std::cell::RefCell::new(None);
}
#[inline]
pub fn env_cfg<T, F>(f: F) -> T
where
	F: FnOnce(&EnvCfg) -> T + 'static
{
	__TARGET_CFG.with(|target_cfg| {
		f(target_cfg.borrow().as_ref().expect("build_cfg has not been initialized with the build script's environment variables. You probably forgot to add `#[build_cfg_main]` to your `fn main()` function."))
	})
}