1use std::collections::BTreeMap;
2use std::str::FromStr;
3use std::sync::Arc;
4use std::{fmt, slice};
5
6#[derive(Clone, Debug, Eq, Hash, PartialEq)]
25pub struct Target {
26 target: target_lexicon::Triple,
27}
28
29impl Target {
30 pub const fn host() -> Self {
34 Self {
35 target: target_lexicon::HOST,
36 }
37 }
38
39 pub fn try_from_target_triple(triple: &str) -> Result<Self, ParseError> {
45 let platform = target_lexicon::Triple::from_str(triple).map_err(ParseError::from)?;
46
47 Ok(Self { target: platform })
48 }
49
50 pub fn from_target_triple_or_unknown(triple: &str) -> Self {
57 let platform = target_lexicon::Triple::from_str(triple)
58 .unwrap_or_else(|_| target_lexicon::Triple::unknown());
59
60 Self { target: platform }
61 }
62}
63
64impl fmt::Display for Target {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 write!(f, "{}", self.target)
67 }
68}
69
70#[derive(Clone, Debug, Eq)]
80pub struct TargetSet {
81 targets: Arc<[Target]>,
82}
83
84impl TargetSet {
85 pub fn len(&self) -> usize {
87 self.targets.len()
88 }
89
90 pub fn is_empty(&self) -> bool {
92 self.targets.is_empty()
93 }
94
95 pub fn contains(&self, target: &Target) -> bool {
97 self.targets.contains(target)
98 }
99
100 pub fn iter(&self) -> slice::Iter<'_, Target> {
102 self.targets.iter()
103 }
104
105 pub fn as_slice(&self) -> &[Target] {
107 &self.targets
108 }
109}
110
111impl Default for TargetSet {
112 fn default() -> Self {
113 Self {
114 targets: Vec::new().into(),
115 }
116 }
117}
118
119impl PartialEq for TargetSet {
120 fn eq(&self, other: &Self) -> bool {
121 Arc::ptr_eq(&self.targets, &other.targets) || self.targets == other.targets
122 }
123}
124
125impl FromIterator<Target> for TargetSet {
126 fn from_iter<I: IntoIterator<Item = Target>>(iter: I) -> Self {
127 let unique = iter
129 .into_iter()
130 .map(|target| (target.to_string(), target))
131 .collect::<BTreeMap<_, _>>();
132
133 Self {
134 targets: unique.into_values().collect(),
135 }
136 }
137}
138
139impl From<Vec<Target>> for TargetSet {
140 fn from(targets: Vec<Target>) -> Self {
141 targets.into_iter().collect()
142 }
143}
144
145impl<'a> IntoIterator for &'a TargetSet {
146 type Item = &'a Target;
147 type IntoIter = slice::Iter<'a, Target>;
148
149 fn into_iter(self) -> Self::IntoIter {
150 self.targets.iter()
151 }
152}
153
154#[derive(Debug, thiserror::Error)]
156#[non_exhaustive]
157pub enum ParseError {
158 #[error("Unknown architecture `{0}`")]
159 Architecture(String),
160 #[error("Unknown vendor `{0}`")]
161 Vendor(String),
162 #[error("Unknown operating system `{0}`")]
163 OperatingSystem(String),
164 #[error("Unknown environment `{0}`")]
165 Environment(String),
166 #[error("Unknown binary format `{0}`")]
167 BinaryFormat(String),
168 #[error("Unknown field `{0}`")]
169 Field(String),
170}
171
172impl From<target_lexicon::ParseError> for ParseError {
173 fn from(value: target_lexicon::ParseError) -> Self {
174 match value {
175 target_lexicon::ParseError::UnrecognizedArchitecture(v) => ParseError::Architecture(v),
176 target_lexicon::ParseError::UnrecognizedVendor(v) => ParseError::Vendor(v),
177 target_lexicon::ParseError::UnrecognizedOperatingSystem(v) => {
178 ParseError::OperatingSystem(v)
179 }
180 target_lexicon::ParseError::UnrecognizedEnvironment(v) => ParseError::Environment(v),
181 target_lexicon::ParseError::UnrecognizedBinaryFormat(v) => ParseError::BinaryFormat(v),
182 target_lexicon::ParseError::UnrecognizedField(v) => ParseError::Field(v),
183 }
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn create_platform() {
193 let this_platform = Target::host();
194
195 let expected = Target {
196 target: target_lexicon::HOST,
197 };
198
199 assert_eq!(this_platform, expected);
200 }
201
202 #[test]
203 fn to_string() {
204 let target = Target::try_from_target_triple("x86_64-unknown-linux-gnu").unwrap();
205
206 assert_eq!(target.to_string(), "x86_64-unknown-linux-gnu");
207 }
208
209 mod target_set {
210 use super::*;
211 use std::ptr;
212
213 fn target(triple: &str) -> Target {
214 Target::from_target_triple_or_unknown(triple)
215 }
216
217 #[test]
218 fn an_empty_set() {
219 let set = TargetSet::default();
220
221 assert!(set.is_empty());
222 assert_eq!(set.len(), 0);
223 }
224
225 #[test]
226 fn a_set_is_ordered_by_target_triple() {
227 let set = [
228 target("x86_64-unknown-linux-gnu"),
229 target("aarch64-apple-darwin"),
230 ]
231 .into_iter()
232 .collect::<TargetSet>();
233
234 let triples = set.iter().map(|t| t.to_string()).collect::<Vec<_>>();
235
236 assert_eq!(
237 triples,
238 ["aarch64-apple-darwin", "x86_64-unknown-linux-gnu"]
239 );
240 }
241
242 #[test]
243 fn a_set_holds_every_target_once() {
244 let set = vec![
245 target("x86_64-unknown-linux-gnu"),
246 target("x86_64-unknown-linux-gnu"),
247 ]
248 .into_iter()
249 .collect::<TargetSet>();
250
251 assert_eq!(set.len(), 1);
252 assert!(set.contains(&target("x86_64-unknown-linux-gnu")));
253 }
254
255 #[test]
256 fn a_set_is_equal_regardless_of_the_order_it_was_built_in() {
257 let left = [
258 target("wasm32-unknown-unknown"),
259 target("aarch64-apple-darwin"),
260 ]
261 .into_iter()
262 .collect::<TargetSet>();
263 let right = [
264 target("aarch64-apple-darwin"),
265 target("wasm32-unknown-unknown"),
266 ]
267 .into_iter()
268 .collect::<TargetSet>();
269
270 assert_eq!(left, right);
271 }
272
273 #[test]
274 fn a_clone_shares_the_targets_of_the_set_it_was_cloned_from() {
275 let set = [target("aarch64-apple-darwin")]
276 .into_iter()
277 .collect::<TargetSet>();
278 let clone = set.clone();
279
280 assert!(ptr::eq(set.as_slice(), clone.as_slice()));
281 }
282 }
283}