1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::error::Error;
6
7#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
9pub struct PlatformTriple(String);
10
11impl PlatformTriple {
12 pub fn new(value: impl AsRef<str>) -> Result<Self, PlatformTripleError> {
18 let trimmed = value.as_ref().trim();
19
20 if trimmed.is_empty() {
21 return Err(PlatformTripleError::Empty);
22 }
23
24 Ok(Self(trimmed.to_string()))
25 }
26
27 #[must_use]
29 pub fn as_str(&self) -> &str {
30 &self.0
31 }
32
33 #[must_use]
35 pub fn into_string(self) -> String {
36 self.0
37 }
38}
39
40impl AsRef<str> for PlatformTriple {
41 fn as_ref(&self) -> &str {
42 self.as_str()
43 }
44}
45
46impl fmt::Display for PlatformTriple {
47 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
48 formatter.write_str(self.as_str())
49 }
50}
51
52impl FromStr for PlatformTriple {
53 type Err = PlatformTripleError;
54
55 fn from_str(value: &str) -> Result<Self, Self::Err> {
56 Self::new(value)
57 }
58}
59
60#[derive(Clone, Copy, Debug, Eq, PartialEq)]
62pub enum PlatformTripleError {
63 Empty,
65}
66
67impl fmt::Display for PlatformTripleError {
68 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
69 match self {
70 Self::Empty => formatter.write_str("platform triple cannot be empty"),
71 }
72 }
73}
74
75impl Error for PlatformTripleError {}
76
77#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
79pub struct Platform {
80 triple: PlatformTriple,
81}
82
83impl Platform {
84 #[must_use]
86 pub const fn new(triple: PlatformTriple) -> Self {
87 Self { triple }
88 }
89
90 #[must_use]
92 pub const fn triple(&self) -> &PlatformTriple {
93 &self.triple
94 }
95
96 #[must_use]
98 pub fn into_triple(self) -> PlatformTriple {
99 self.triple
100 }
101}
102
103impl From<PlatformTriple> for Platform {
104 fn from(triple: PlatformTriple) -> Self {
105 Self::new(triple)
106 }
107}
108
109impl fmt::Display for Platform {
110 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
111 self.triple.fmt(formatter)
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::{Platform, PlatformTriple, PlatformTripleError};
118
119 #[test]
120 fn accepts_common_platform_triples() {
121 for triple in [
122 "x86_64-unknown-linux-gnu",
123 "aarch64-apple-darwin",
124 "wasm32-wasip1",
125 ] {
126 assert_eq!(PlatformTriple::new(triple).unwrap().as_str(), triple);
127 }
128 }
129
130 #[test]
131 fn rejects_empty_platform_triples() {
132 assert_eq!(PlatformTriple::new(" "), Err(PlatformTripleError::Empty));
133 }
134
135 #[test]
136 fn display_round_trips_through_parse() {
137 let triple = PlatformTriple::new("x86_64-pc-windows-msvc").unwrap();
138 let round_trip: PlatformTriple = triple.to_string().parse().unwrap();
139
140 assert_eq!(round_trip, triple);
141 }
142
143 #[test]
144 fn platform_wraps_a_triple_without_detection() {
145 let triple = PlatformTriple::new("aarch64-apple-darwin").unwrap();
146 let platform = Platform::new(triple.clone());
147
148 assert_eq!(platform.triple(), &triple);
149 assert_eq!(platform.to_string(), "aarch64-apple-darwin");
150 }
151}