1use crate::error::{HasAtomicParseError, Reason};
2use std::{borrow::Cow, ops::Deref};
3
4mod builtins;
5
6pub use builtins::ALL_BUILTINS;
9
10#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
12pub struct Triple(pub Cow<'static, str>);
13
14#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
16pub struct Abi(pub Cow<'static, str>);
17
18#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
20pub struct Arch(pub Cow<'static, str>);
21
22#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
24pub struct Vendor(pub Cow<'static, str>);
25
26#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
29pub struct Os(pub Cow<'static, str>);
30
31#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
34pub struct Family(pub Cow<'static, str>);
35
36#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
40pub struct Env(pub Cow<'static, str>);
41
42#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
44pub struct Panic(pub Cow<'static, str>);
45
46macro_rules! field_impls {
47    ($kind:ident) => {
48        impl $kind {
49            #[inline]
53            pub fn new(val: impl Into<Cow<'static, str>>) -> Self {
54                Self(val.into())
55            }
56
57            #[inline]
59            pub const fn new_const(val: &'static str) -> Self {
60                Self(Cow::Borrowed(val))
61            }
62
63            #[inline]
65            pub fn as_str(&self) -> &str {
66                &*self.0
67            }
68        }
69
70        impl AsRef<str> for $kind {
71            #[inline]
72            fn as_ref(&self) -> &str {
73                &*self.0
74            }
75        }
76
77        impl std::fmt::Display for $kind {
78            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79                f.write_str(self.as_str())
80            }
81        }
82    };
83}
84
85field_impls!(Triple);
86field_impls!(Abi);
87field_impls!(Arch);
88field_impls!(Vendor);
89field_impls!(Os);
90field_impls!(Family);
91field_impls!(Env);
92field_impls!(Panic);
93
94#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
96#[non_exhaustive]
97pub enum HasAtomic {
98    IntegerSize(u16),
101
102    Pointer,
104}
105
106impl std::str::FromStr for HasAtomic {
107    type Err = HasAtomicParseError;
108
109    fn from_str(s: &str) -> Result<Self, Self::Err> {
110        if let Ok(size) = s.parse::<u16>() {
111            Ok(Self::IntegerSize(size))
112        } else if s == "ptr" {
113            Ok(HasAtomic::Pointer)
114        } else {
115            Err(HasAtomicParseError {
116                input: s.to_owned(),
117            })
118        }
119    }
120}
121
122impl std::fmt::Display for HasAtomic {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        match self {
125            Self::IntegerSize(size) => write!(f, "{size}"),
126            Self::Pointer => write!(f, "ptr"),
127        }
128    }
129}
130
131#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
135pub struct Families(Cow<'static, [Family]>);
136
137impl Families {
138    #[inline]
144    pub fn new(val: impl IntoIterator<Item = Family>) -> Self {
145        let mut fams: Vec<_> = val.into_iter().collect();
146        fams.sort_unstable();
147        Self(Cow::Owned(fams))
148    }
149
150    #[inline]
155    pub const fn new_const(val: &'static [Family]) -> Self {
156        Self(Cow::Borrowed(val))
158    }
159
160    #[inline]
162    pub fn contains(&self, val: &Family) -> bool {
163        self.0.contains(val)
164    }
165}
166
167impl Deref for Families {
168    type Target = [Family];
169    fn deref(&self) -> &Self::Target {
170        &self.0
171    }
172}
173
174impl AsRef<[Family]> for Families {
175    #[inline]
176    fn as_ref(&self) -> &[Family] {
177        &self.0
178    }
179}
180
181impl std::fmt::Display for Families {
182    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183        write!(f, "{{")?;
184        let len = self.0.len();
185        for (idx, family) in self.0.iter().enumerate() {
186            write!(f, "{family}")?;
187            if idx + 1 < len {
188                write!(f, ", ")?;
189            }
190        }
191        write!(f, "}}")
192    }
193}
194
195#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
197pub struct HasAtomics(Cow<'static, [HasAtomic]>);
198
199impl HasAtomics {
200    #[inline]
204    pub fn new(val: impl IntoIterator<Item = HasAtomic>) -> Self {
205        let mut has_atomics: Vec<_> = val.into_iter().collect();
206        has_atomics.sort_unstable();
207        Self(Cow::Owned(has_atomics))
208    }
209
210    #[inline]
215    pub const fn new_const(val: &'static [HasAtomic]) -> Self {
216        Self(Cow::Borrowed(val))
218    }
219
220    #[inline]
222    pub fn contains(&self, val: HasAtomic) -> bool {
223        self.0.contains(&val)
224    }
225}
226
227impl Deref for HasAtomics {
228    type Target = [HasAtomic];
229    fn deref(&self) -> &Self::Target {
230        &self.0
231    }
232}
233
234impl AsRef<[HasAtomic]> for HasAtomics {
235    #[inline]
236    fn as_ref(&self) -> &[HasAtomic] {
237        &self.0
238    }
239}
240
241impl std::fmt::Display for HasAtomics {
242    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243        write!(f, "{{")?;
244        let len = self.0.len();
245        for (idx, has_atomic) in self.0.iter().enumerate() {
246            write!(f, "{has_atomic}")?;
247            if idx + 1 < len {
248                write!(f, ", ")?;
249            }
250        }
251        write!(f, "}}")
252    }
253}
254
255macro_rules! target_enum {
256    (
257        $(#[$outer:meta])*
258        pub enum $kind:ident {
259            $(
260                $(#[$inner:ident $($args:tt)*])*
261                $name:ident $(= $value:expr)?,
262            )+
263        }
264    ) => {
265        $(#[$outer])*
266        #[allow(non_camel_case_types)]
267        pub enum $kind {
268            $(
269                $(#[$inner $($args)*])*
270                $name $(= $value)?,
271            )+
272        }
273
274        impl_from_str! {
275            $kind {
276                $(
277                    $(#[$inner $($args)*])*
278                    $name $(= $value)?,
279                )+
280            }
281        }
282    };
283}
284
285macro_rules! impl_from_str {
286    (
287        $kind:ident {
288            $(
289                $(#[$attr:ident $($args:tt)*])*
290                $name:ident $(= $value:expr)?,
291            )+
292        }
293    ) => {
294        impl std::str::FromStr for $kind {
295            type Err = Reason;
296            fn from_str(s: &str) -> Result<Self, Self::Err> {
297                match s {
298                    $(stringify!($name) => Ok(Self::$name),)+
299                    _ => Err(Reason::Unexpected(&[$(stringify!($name),)+])),
300                }
301            }
302        }
303    };
304}
305
306target_enum! {
307    #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
309    pub enum Endian {
310        big,
311        little,
312    }
313}
314
315#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
317pub struct TargetInfo {
318    pub triple: Triple,
320    pub os: Option<Os>,
324    pub abi: Option<Abi>,
327    pub arch: Arch,
331    pub env: Option<Env>,
335    pub vendor: Option<Vendor>,
339    pub families: Families,
343    pub pointer_width: u8,
347    pub endian: Endian,
351    pub has_atomics: HasAtomics,
353    pub panic: Panic,
356}
357
358pub fn get_builtin_target_by_triple(triple: &str) -> Option<&'static TargetInfo> {
364    ALL_BUILTINS
365        .binary_search_by(|ti| ti.triple.as_ref().cmp(triple))
366        .map(|i| &ALL_BUILTINS[i])
367        .ok()
368}
369
370pub fn rustc_version() -> &'static str {
375    builtins::RUSTC_VERSION
376}
377
378#[cfg(test)]
379mod test {
380    use crate::targets::get_builtin_target_by_triple;
381    use std::collections::{BTreeSet, HashSet};
382
383    #[test]
386    fn targets_are_sorted() {
387        for window in super::ALL_BUILTINS.windows(2) {
388            assert!(window[0].triple < window[1].triple);
389        }
390    }
391
392    #[test]
395    fn has_ios() {
396        assert_eq!(
397            8,
398            super::ALL_BUILTINS
399                .iter()
400                .filter(|ti| ti.os == Some(super::Os::ios))
401                .count()
402        );
403    }
404
405    #[test]
407    fn set_map_key() {
408        let target_info =
409            get_builtin_target_by_triple("x86_64-unknown-linux-gnu").expect("known target");
410
411        let mut btree_set = BTreeSet::new();
412        btree_set.insert(target_info);
413
414        let mut hash_set = HashSet::new();
415        hash_set.insert(target_info);
416    }
417
418    #[test]
419    fn family_comp() {
420        let a = super::Families::new([super::Family::unix, super::Family::wasm]);
421        let b = super::Families::new([super::Family::wasm, super::Family::unix]);
422
423        assert_eq!(a, b);
424    }
425}