1use crate::{
5 Error, Platform,
6 errors::{RustcVersionVerboseParseError, TripleParseError},
7};
8use cfg_expr::{
9 TargetPredicate,
10 expr::TargetMatcher,
11 target_lexicon,
12 targets::{TargetInfo, get_builtin_target_by_triple},
13};
14use std::{borrow::Cow, cmp::Ordering, hash, str::FromStr};
15
16#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
53pub struct Triple {
54 inner: TripleInner,
55}
56
57impl Triple {
58 pub fn new(triple_str: impl Into<Cow<'static, str>>) -> Result<Self, TripleParseError> {
60 let inner = TripleInner::new(triple_str.into())?;
61 Ok(Self { inner })
62 }
63
64 pub fn new_strict(triple_str: impl Into<Cow<'static, str>>) -> Result<Self, TripleParseError> {
69 let inner = TripleInner::new_strict(triple_str.into())?;
70 Ok(Self { inner })
71 }
72
73 pub fn from_rustc_version_verbose(output: impl AsRef<[u8]>) -> Result<Self, Error> {
91 let output_slice = output.as_ref();
92 let output = std::str::from_utf8(output_slice).map_err(|_| {
93 let output_vec = output_slice.to_vec();
96 Error::RustcVersionVerboseParse(RustcVersionVerboseParseError::InvalidUtf8(
97 String::from_utf8(output_vec)
98 .expect_err("we just failed to convert to UTF-8 above"),
99 ))
100 })?;
101
102 let triple_str = output
105 .lines()
106 .find_map(|line| line.strip_prefix("host: "))
107 .ok_or_else(|| {
108 Error::RustcVersionVerboseParse(RustcVersionVerboseParseError::MissingHostLine {
109 output: output.to_owned(),
110 })
111 })?;
112
113 Self::new(triple_str.to_owned()).map_err(Error::UnknownPlatformTriple)
115 }
116
117 #[cfg(feature = "custom")]
119 pub fn new_custom(
120 triple_str: impl Into<Cow<'static, str>>,
121 json: &str,
122 ) -> Result<Self, crate::errors::CustomTripleCreateError> {
123 use crate::custom::TargetDefinition;
124
125 let triple_str = triple_str.into();
126 let target_def: TargetDefinition = serde_json::from_str(json).map_err(|error| {
127 crate::errors::CustomTripleCreateError::DeserializeJson {
128 triple: triple_str.to_string(),
129 input: json.to_string(),
130 error: error.into(),
131 }
132 })?;
133 #[cfg(feature = "summaries")]
134 let minified_json =
135 serde_json::to_string(&target_def).expect("serialization is infallible");
136
137 let target_info = Box::new(target_def.into_target_info(triple_str));
138 Ok(Self {
139 inner: TripleInner::Custom {
140 target_info,
141 #[cfg(feature = "summaries")]
142 json: minified_json,
143 },
144 })
145 }
146
147 #[inline]
149 pub fn as_str(&self) -> &str {
150 self.inner.as_str()
151 }
152
153 pub fn is_standard(&self) -> bool {
167 self.inner.is_standard()
168 }
169
170 #[inline]
182 pub fn is_builtin(&self) -> bool {
183 self.inner.is_builtin()
184 }
185
186 pub fn is_heuristic(&self) -> bool {
202 self.inner.is_heuristic()
203 }
204
205 pub fn is_custom(&self) -> bool {
210 self.inner.is_custom()
211 }
212
213 #[inline]
218 pub fn eval(&self, platform: &Platform) -> bool {
219 self.as_str() == platform.triple_str()
220 }
221
222 #[inline]
224 pub(crate) fn matches(&self, tp: &TargetPredicate) -> bool {
225 self.inner.matches(tp)
226 }
227
228 #[cfg(feature = "summaries")]
229 pub(crate) fn custom_json(&self) -> Option<&str> {
230 self.inner.custom_json()
231 }
232}
233
234impl FromStr for Triple {
235 type Err = TripleParseError;
236
237 fn from_str(triple_str: &str) -> Result<Self, Self::Err> {
238 let inner = TripleInner::from_borrowed_str(triple_str)?;
239 Ok(Self { inner })
240 }
241}
242
243#[derive(Clone, Debug)]
245enum TripleInner {
246 Builtin(&'static TargetInfo),
248
249 #[cfg(feature = "custom")]
251 Custom {
252 target_info: Box<cfg_expr::targets::TargetInfo>,
253 #[cfg(feature = "summaries")]
255 json: String,
256 },
257
258 Lexicon {
260 triple_str: Cow<'static, str>,
261 lexicon_triple: target_lexicon::Triple,
262 },
263}
264
265impl TripleInner {
266 fn new(triple_str: Cow<'static, str>) -> Result<Self, TripleParseError> {
267 if let Some(target_info) = get_builtin_target_by_triple(&triple_str) {
269 return Ok(TripleInner::Builtin(target_info));
270 }
271
272 match triple_str.parse::<target_lexicon::Triple>() {
274 Ok(lexicon_triple) => Ok(TripleInner::Lexicon {
275 triple_str,
276 lexicon_triple,
277 }),
278 Err(lexicon_err) => Err(TripleParseError::new(triple_str, lexicon_err)),
279 }
280 }
281
282 fn new_strict(triple_str: Cow<'static, str>) -> Result<Self, TripleParseError> {
283 if let Some(target_info) = get_builtin_target_by_triple(&triple_str) {
284 return Ok(TripleInner::Builtin(target_info));
285 }
286 Err(TripleParseError::new_strict(triple_str))
287 }
288
289 fn from_borrowed_str(triple_str: &str) -> Result<Self, TripleParseError> {
290 if let Some(target_info) = get_builtin_target_by_triple(triple_str) {
292 return Ok(TripleInner::Builtin(target_info));
293 }
294
295 match triple_str.parse::<target_lexicon::Triple>() {
297 Ok(lexicon_triple) => Ok(TripleInner::Lexicon {
298 triple_str: triple_str.to_owned().into(),
299 lexicon_triple,
300 }),
301 Err(lexicon_err) => Err(TripleParseError::new(
302 triple_str.to_owned().into(),
303 lexicon_err,
304 )),
305 }
306 }
307
308 fn is_standard(&self) -> bool {
309 match self {
310 TripleInner::Builtin(_) | TripleInner::Lexicon { .. } => true,
311 #[cfg(feature = "custom")]
312 TripleInner::Custom { .. } => false,
313 }
314 }
315
316 fn is_builtin(&self) -> bool {
317 match self {
318 TripleInner::Builtin(_) => true,
319 TripleInner::Lexicon { .. } => false,
320 #[cfg(feature = "custom")]
321 TripleInner::Custom { .. } => false,
322 }
323 }
324
325 fn is_heuristic(&self) -> bool {
326 match self {
327 TripleInner::Builtin(_) => false,
328 TripleInner::Lexicon { .. } => true,
329 #[cfg(feature = "custom")]
330 TripleInner::Custom { .. } => false,
331 }
332 }
333
334 fn is_custom(&self) -> bool {
335 match self {
336 TripleInner::Builtin(_) | TripleInner::Lexicon { .. } => false,
337 #[cfg(feature = "custom")]
338 TripleInner::Custom { .. } => true,
339 }
340 }
341
342 fn as_str(&self) -> &str {
343 match self {
344 TripleInner::Builtin(target_info) => target_info.triple.as_str(),
345 #[cfg(feature = "custom")]
346 TripleInner::Custom { target_info, .. } => target_info.triple.as_str(),
347 TripleInner::Lexicon { triple_str, .. } => triple_str,
348 }
349 }
350
351 fn matches(&self, tp: &TargetPredicate) -> bool {
352 match self {
353 TripleInner::Builtin(target_info) => target_info.matches(tp),
354 #[cfg(feature = "custom")]
355 TripleInner::Custom { target_info, .. } => target_info.matches(tp),
356 TripleInner::Lexicon { lexicon_triple, .. } => lexicon_triple.matches(tp),
357 }
358 }
359
360 #[cfg(feature = "summaries")]
361 pub(crate) fn custom_json(&self) -> Option<&str> {
362 match self {
363 TripleInner::Builtin(_) => None,
364 #[cfg(feature = "custom")]
365 TripleInner::Custom { json, .. } => Some(json),
366 TripleInner::Lexicon { .. } => None,
367 }
368 }
369
370 fn project(&self) -> TripleInnerProjected<'_> {
371 match self {
372 TripleInner::Builtin(target_info) => {
373 TripleInnerProjected::Builtin(target_info.triple.as_str())
374 }
375 #[cfg(feature = "custom")]
376 TripleInner::Custom { target_info, .. } => TripleInnerProjected::Custom(target_info),
377 TripleInner::Lexicon { triple_str, .. } => TripleInnerProjected::Lexicon(triple_str),
378 }
379 }
380}
381
382#[derive(Eq, PartialEq, PartialOrd, Ord, Hash)]
384enum TripleInnerProjected<'a> {
385 Builtin(&'a str),
387 #[cfg(feature = "custom")]
388 Custom(&'a TargetInfo),
389 Lexicon(&'a str),
390}
391
392impl PartialEq for TripleInner {
393 fn eq(&self, other: &Self) -> bool {
394 self.project().eq(&other.project())
395 }
396}
397
398impl Eq for TripleInner {}
399
400impl PartialOrd for TripleInner {
401 #[inline]
402 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
403 Some(self.cmp(other))
404 }
405}
406
407impl Ord for TripleInner {
408 #[inline]
409 fn cmp(&self, other: &Self) -> Ordering {
410 self.project().cmp(&other.project())
411 }
412}
413
414impl hash::Hash for TripleInner {
415 fn hash<H: hash::Hasher>(&self, state: &mut H) {
416 hash::Hash::hash(&self.project(), state);
417 }
418}
419
420#[cfg(test)]
421mod tests {
422 use super::*;
423 use target_lexicon::*;
424
425 #[test]
426 fn test_parse() {
427 let target =
428 super::Triple::new("x86_64-pc-darwin").expect("this triple is known to target-lexicon");
429
430 let expected_triple = target_lexicon::Triple {
431 architecture: Architecture::X86_64,
432 vendor: Vendor::Pc,
433 operating_system: OperatingSystem::Darwin(None),
434 environment: Environment::Unknown,
435 binary_format: BinaryFormat::Macho,
436 };
437
438 let actual_triple = match target.inner {
439 TripleInner::Lexicon { lexicon_triple, .. } => lexicon_triple,
440 TripleInner::Builtin(_) => {
441 panic!("should not have been able to parse x86_64-pc-darwin as a builtin");
442 }
443 #[cfg(feature = "custom")]
444 TripleInner::Custom { .. } => {
445 panic!("not a custom platform")
446 }
447 };
448 assert_eq!(
449 actual_triple, expected_triple,
450 "lexicon triple matched correctly"
451 );
452 }
453
454 #[test]
455 fn test_parse_rustc_version_verbose() {
456 let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string());
457 let output = std::process::Command::new(rustc)
458 .arg("-vV")
459 .output()
460 .expect("rustc -vV is successful");
461 if !output.status.success() {
462 panic!("rustc -vV failed: {:?}", output);
463 }
464
465 let triple = super::Triple::from_rustc_version_verbose(output.stdout).unwrap();
466 assert!(triple.is_standard());
467 }
468}