use crate::{errors::TripleParseError, Platform};
use cfg_expr::{
expr::TargetMatcher,
target_lexicon,
targets::{get_builtin_target_by_triple, TargetInfo},
TargetPredicate,
};
use std::{borrow::Cow, cmp::Ordering, hash, str::FromStr};
#[derive(Clone, Debug)]
pub struct Triple {
inner: TripleInner,
}
impl Triple {
pub fn new(triple_str: impl Into<Cow<'static, str>>) -> Result<Self, TripleParseError> {
let inner = TripleInner::new(triple_str.into())?;
Ok(Self { inner })
}
#[inline]
pub fn as_str(&self) -> &str {
self.inner.as_str()
}
#[inline]
pub fn eval(&self, platform: &Platform) -> bool {
self == platform.triple()
}
#[inline]
pub(crate) fn matches(&self, tp: &TargetPredicate) -> bool {
self.inner.matches(tp)
}
}
impl FromStr for Triple {
type Err = TripleParseError;
fn from_str(triple_str: &str) -> Result<Self, Self::Err> {
let inner = TripleInner::from_borrowed_str(triple_str)?;
Ok(Self { inner })
}
}
#[derive(Clone, Debug)]
enum TripleInner {
Builtin(&'static TargetInfo),
Lexicon {
triple_str: Cow<'static, str>,
lexicon_triple: target_lexicon::Triple,
},
}
impl TripleInner {
fn new(triple_str: Cow<'static, str>) -> Result<Self, TripleParseError> {
if let Some(target_info) = get_builtin_target_by_triple(&triple_str) {
return Ok(TripleInner::Builtin(target_info));
}
match triple_str.parse::<target_lexicon::Triple>() {
Ok(lexicon_triple) => Ok(TripleInner::Lexicon {
triple_str,
lexicon_triple,
}),
Err(lexicon_err) => Err(TripleParseError::new(triple_str, lexicon_err)),
}
}
fn from_borrowed_str(triple_str: &str) -> Result<Self, TripleParseError> {
if let Some(target_info) = get_builtin_target_by_triple(triple_str) {
return Ok(TripleInner::Builtin(target_info));
}
match triple_str.parse::<target_lexicon::Triple>() {
Ok(lexicon_triple) => Ok(TripleInner::Lexicon {
triple_str: triple_str.to_owned().into(),
lexicon_triple,
}),
Err(lexicon_err) => Err(TripleParseError::new(
triple_str.to_owned().into(),
lexicon_err,
)),
}
}
fn as_str(&self) -> &str {
match self {
TripleInner::Builtin(target_info) => target_info.triple.as_str(),
TripleInner::Lexicon { triple_str, .. } => triple_str,
}
}
fn matches(&self, tp: &TargetPredicate) -> bool {
match self {
TripleInner::Builtin(target_info) => target_info.matches(tp),
TripleInner::Lexicon { lexicon_triple, .. } => lexicon_triple.matches(tp),
}
}
}
impl PartialEq for Triple {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_str().eq(other.as_str())
}
}
impl Eq for Triple {}
impl PartialOrd for Triple {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.as_str().partial_cmp(other.as_str())
}
}
impl Ord for Triple {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.as_str().cmp(other.as_str())
}
}
impl hash::Hash for Triple {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
hash::Hash::hash(self.as_str(), state);
}
}
#[cfg(test)]
mod tests {
use super::*;
use target_lexicon::*;
#[test]
fn test_parse() {
let target =
super::Triple::new("x86_64-pc-darwin").expect("this triple is known to target-lexicon");
let expected_triple = target_lexicon::Triple {
architecture: Architecture::X86_64,
vendor: Vendor::Pc,
operating_system: OperatingSystem::Darwin,
environment: Environment::Unknown,
binary_format: BinaryFormat::Macho,
};
let actual_triple = match target.inner {
TripleInner::Lexicon { lexicon_triple, .. } => lexicon_triple,
TripleInner::Builtin(_) => {
panic!("should not have been able to parse x86_64-pc-darwin as a builtin");
}
};
assert_eq!(
actual_triple, expected_triple,
"lexicon triple matched correctly"
);
}
}