use crate::error::{HasAtomicParseError, Reason};
use std::{borrow::Cow, ops::Deref};
mod builtins;
pub use builtins::ALL_BUILTINS;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Triple(pub Cow<'static, str>);
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Abi(pub Cow<'static, str>);
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Arch(pub Cow<'static, str>);
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Vendor(pub Cow<'static, str>);
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Os(pub Cow<'static, str>);
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Family(pub Cow<'static, str>);
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Env(pub Cow<'static, str>);
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Panic(pub Cow<'static, str>);
macro_rules! field_impls {
($kind:ident) => {
impl $kind {
#[inline]
pub fn new(val: impl Into<Cow<'static, str>>) -> Self {
Self(val.into())
}
#[inline]
pub const fn new_const(val: &'static str) -> Self {
Self(Cow::Borrowed(val))
}
#[inline]
pub fn as_str(&self) -> &str {
&*self.0
}
}
impl AsRef<str> for $kind {
#[inline]
fn as_ref(&self) -> &str {
&*self.0
}
}
impl std::fmt::Display for $kind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
};
}
field_impls!(Triple);
field_impls!(Abi);
field_impls!(Arch);
field_impls!(Vendor);
field_impls!(Os);
field_impls!(Family);
field_impls!(Env);
field_impls!(Panic);
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum HasAtomic {
IntegerSize(u16),
Pointer,
}
impl std::str::FromStr for HasAtomic {
type Err = HasAtomicParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(size) = s.parse::<u16>() {
Ok(Self::IntegerSize(size))
} else if s == "ptr" {
Ok(HasAtomic::Pointer)
} else {
Err(HasAtomicParseError {
input: s.to_owned(),
})
}
}
}
impl std::fmt::Display for HasAtomic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::IntegerSize(size) => write!(f, "{size}"),
Self::Pointer => write!(f, "ptr"),
}
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Families(Cow<'static, [Family]>);
impl Families {
#[inline]
pub fn new(val: impl IntoIterator<Item = Family>) -> Self {
let mut fams: Vec<_> = val.into_iter().collect();
fams.sort_unstable();
Self(Cow::Owned(fams))
}
#[inline]
pub const fn new_const(val: &'static [Family]) -> Self {
Self(Cow::Borrowed(val))
}
#[inline]
pub fn contains(&self, val: &Family) -> bool {
self.0.contains(val)
}
}
impl Deref for Families {
type Target = [Family];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<[Family]> for Families {
#[inline]
fn as_ref(&self) -> &[Family] {
&self.0
}
}
impl std::fmt::Display for Families {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{{")?;
let len = self.0.len();
for (idx, family) in self.0.iter().enumerate() {
write!(f, "{family}")?;
if idx + 1 < len {
write!(f, ", ")?;
}
}
write!(f, "}}")
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct HasAtomics(Cow<'static, [HasAtomic]>);
impl HasAtomics {
#[inline]
pub fn new(val: impl IntoIterator<Item = HasAtomic>) -> Self {
let mut has_atomics: Vec<_> = val.into_iter().collect();
has_atomics.sort_unstable();
Self(Cow::Owned(has_atomics))
}
#[inline]
pub const fn new_const(val: &'static [HasAtomic]) -> Self {
Self(Cow::Borrowed(val))
}
#[inline]
pub fn contains(&self, val: HasAtomic) -> bool {
self.0.contains(&val)
}
}
impl Deref for HasAtomics {
type Target = [HasAtomic];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<[HasAtomic]> for HasAtomics {
#[inline]
fn as_ref(&self) -> &[HasAtomic] {
&self.0
}
}
impl std::fmt::Display for HasAtomics {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{{")?;
let len = self.0.len();
for (idx, has_atomic) in self.0.iter().enumerate() {
write!(f, "{has_atomic}")?;
if idx + 1 < len {
write!(f, ", ")?;
}
}
write!(f, "}}")
}
}
macro_rules! target_enum {
(
$(#[$outer:meta])*
pub enum $kind:ident {
$(
$(#[$inner:ident $($args:tt)*])*
$name:ident $(= $value:expr)?,
)+
}
) => {
$(#[$outer])*
#[allow(non_camel_case_types)]
pub enum $kind {
$(
$(#[$inner $($args)*])*
$name $(= $value)?,
)+
}
impl_from_str! {
$kind {
$(
$(#[$inner $($args)*])*
$name $(= $value)?,
)+
}
}
};
}
macro_rules! impl_from_str {
(
$kind:ident {
$(
$(#[$attr:ident $($args:tt)*])*
$name:ident $(= $value:expr)?,
)+
}
) => {
impl std::str::FromStr for $kind {
type Err = Reason;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
$(stringify!($name) => Ok(Self::$name),)+
_ => Err(Reason::Unexpected(&[$(stringify!($name),)+])),
}
}
}
};
}
target_enum! {
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Endian {
big,
little,
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct TargetInfo {
pub triple: Triple,
pub os: Option<Os>,
pub abi: Option<Abi>,
pub arch: Arch,
pub env: Option<Env>,
pub vendor: Option<Vendor>,
pub families: Families,
pub pointer_width: u8,
pub endian: Endian,
pub has_atomics: HasAtomics,
pub panic: Panic,
}
pub fn get_builtin_target_by_triple(triple: &str) -> Option<&'static TargetInfo> {
ALL_BUILTINS
.binary_search_by(|ti| ti.triple.as_ref().cmp(triple))
.map(|i| &ALL_BUILTINS[i])
.ok()
}
pub fn rustc_version() -> &'static str {
builtins::RUSTC_VERSION
}
#[cfg(test)]
mod test {
use crate::targets::get_builtin_target_by_triple;
use std::collections::{BTreeSet, HashSet};
#[test]
fn targets_are_sorted() {
for window in super::ALL_BUILTINS.windows(2) {
assert!(window[0].triple < window[1].triple);
}
}
#[test]
fn has_ios() {
assert_eq!(
8,
super::ALL_BUILTINS
.iter()
.filter(|ti| ti.os == Some(super::Os::ios))
.count()
);
}
#[test]
fn set_map_key() {
let target_info =
get_builtin_target_by_triple("x86_64-unknown-linux-gnu").expect("known target");
let mut btree_set = BTreeSet::new();
btree_set.insert(target_info);
let mut hash_set = HashSet::new();
hash_set.insert(target_info);
}
#[test]
fn family_comp() {
let a = super::Families::new([super::Family::unix, super::Family::wasm]);
let b = super::Families::new([super::Family::wasm, super::Family::unix]);
assert_eq!(a, b);
}
}