1use crate::Error;
2use std::str::FromStr;
3
4#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, Ord, PartialOrd)]
6pub enum ArchVariant {
7 V2,
10 V3,
13 V4,
16}
17
18#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
19pub struct Arch {
20 pub(crate) family: target_lexicon::Architecture,
21 pub(crate) variant: Option<ArchVariant>,
22}
23
24impl Ord for Arch {
25 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
26 if self.family == other.family {
27 return self.variant.cmp(&other.variant);
28 }
29
30 let preferred = if cfg!(all(windows, target_arch = "aarch64")) {
42 Self {
43 family: target_lexicon::Architecture::X86_64,
44 variant: None,
45 }
46 } else {
47 Self::from_env()
49 };
50
51 match (
52 self.family == preferred.family,
53 other.family == preferred.family,
54 ) {
55 (true, true) => unreachable!(),
56 (true, false) => std::cmp::Ordering::Less,
57 (false, true) => std::cmp::Ordering::Greater,
58 (false, false) => {
59 self.family.to_string().cmp(&other.family.to_string())
61 }
62 }
63 }
64}
65
66impl PartialOrd for Arch {
67 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
68 Some(self.cmp(other))
69 }
70}
71impl Arch {
72 pub fn new(family: target_lexicon::Architecture, variant: Option<ArchVariant>) -> Self {
73 Self { family, variant }
74 }
75
76 pub fn from_env() -> Self {
77 #[cfg(test)]
78 {
79 if let Some(arch) = test_support::get_mock_arch() {
80 return arch;
81 }
82 }
83
84 Self {
85 family: target_lexicon::HOST.architecture,
86 variant: None,
87 }
88 }
89
90 pub fn family(&self) -> target_lexicon::Architecture {
91 self.family
92 }
93
94 pub fn is_arm(&self) -> bool {
95 matches!(self.family, target_lexicon::Architecture::Arm(_))
96 }
97
98 pub fn is_wasm(&self) -> bool {
99 matches!(self.family, target_lexicon::Architecture::Wasm32)
100 }
101}
102
103impl std::fmt::Display for Arch {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 match self.family {
106 target_lexicon::Architecture::X86_32(target_lexicon::X86_32Architecture::I686) => {
107 write!(f, "x86")?;
108 }
109 inner => write!(f, "{inner}")?,
110 }
111 if let Some(variant) = self.variant {
112 write!(f, "_{variant}")?;
113 }
114 Ok(())
115 }
116}
117
118impl FromStr for Arch {
119 type Err = Error;
120
121 fn from_str(s: &str) -> Result<Self, Self::Err> {
122 fn parse_family(s: &str) -> Result<target_lexicon::Architecture, Error> {
123 let inner = match s {
124 "x86" => {
127 target_lexicon::Architecture::X86_32(target_lexicon::X86_32Architecture::I686)
128 }
129 _ => target_lexicon::Architecture::from_str(s)
130 .map_err(|()| Error::UnknownArch(s.to_string()))?,
131 };
132 if matches!(inner, target_lexicon::Architecture::Unknown) {
133 return Err(Error::UnknownArch(s.to_string()));
134 }
135 Ok(inner)
136 }
137
138 if let Some((Ok(family), Ok(variant))) = s
140 .rsplit_once('_')
141 .map(|(family, variant)| (parse_family(family), ArchVariant::from_str(variant)))
142 {
143 if !matches!(family, target_lexicon::Architecture::X86_64) {
145 return Err(Error::UnsupportedVariant(
146 variant.to_string(),
147 family.to_string(),
148 ));
149 }
150 return Ok(Self {
151 family,
152 variant: Some(variant),
153 });
154 }
155
156 let family = parse_family(s)?;
157
158 Ok(Self {
159 family,
160 variant: None,
161 })
162 }
163}
164
165impl FromStr for ArchVariant {
166 type Err = ();
167
168 fn from_str(s: &str) -> Result<Self, Self::Err> {
169 match s {
170 "v2" => Ok(Self::V2),
171 "v3" => Ok(Self::V3),
172 "v4" => Ok(Self::V4),
173 _ => Err(()),
174 }
175 }
176}
177
178impl std::fmt::Display for ArchVariant {
179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 match self {
181 Self::V2 => write!(f, "v2"),
182 Self::V3 => write!(f, "v3"),
183 Self::V4 => write!(f, "v4"),
184 }
185 }
186}
187
188impl From<&uv_platform_tags::Arch> for Arch {
189 fn from(value: &uv_platform_tags::Arch) -> Self {
190 match value {
191 uv_platform_tags::Arch::Aarch64 => Self::new(
192 target_lexicon::Architecture::Aarch64(target_lexicon::Aarch64Architecture::Aarch64),
193 None,
194 ),
195 uv_platform_tags::Arch::Armv5TEL => Self::new(
196 target_lexicon::Architecture::Arm(target_lexicon::ArmArchitecture::Armv5te),
197 None,
198 ),
199 uv_platform_tags::Arch::Armv6L => Self::new(
200 target_lexicon::Architecture::Arm(target_lexicon::ArmArchitecture::Armv6),
201 None,
202 ),
203 uv_platform_tags::Arch::Armv7L => Self::new(
204 target_lexicon::Architecture::Arm(target_lexicon::ArmArchitecture::Armv7),
205 None,
206 ),
207 uv_platform_tags::Arch::S390X => Self::new(target_lexicon::Architecture::S390x, None),
208 uv_platform_tags::Arch::Powerpc => {
209 Self::new(target_lexicon::Architecture::Powerpc, None)
210 }
211 uv_platform_tags::Arch::Powerpc64 => {
212 Self::new(target_lexicon::Architecture::Powerpc64, None)
213 }
214 uv_platform_tags::Arch::Powerpc64Le => {
215 Self::new(target_lexicon::Architecture::Powerpc64le, None)
216 }
217 uv_platform_tags::Arch::X86 => Self::new(
218 target_lexicon::Architecture::X86_32(target_lexicon::X86_32Architecture::I686),
219 None,
220 ),
221 uv_platform_tags::Arch::X86_64 => Self::new(target_lexicon::Architecture::X86_64, None),
222 uv_platform_tags::Arch::LoongArch64 => {
223 Self::new(target_lexicon::Architecture::LoongArch64, None)
224 }
225 uv_platform_tags::Arch::Riscv64 => Self::new(
226 target_lexicon::Architecture::Riscv64(target_lexicon::Riscv64Architecture::Riscv64),
227 None,
228 ),
229 uv_platform_tags::Arch::Wasm32 => Self::new(target_lexicon::Architecture::Wasm32, None),
230 }
231 }
232}
233
234#[cfg(test)]
235pub(crate) mod test_support {
236 use super::*;
237 use std::cell::RefCell;
238
239 thread_local! {
240 static MOCK_ARCH: RefCell<Option<Arch>> = const { RefCell::new(None) };
241 }
242
243 pub(crate) fn get_mock_arch() -> Option<Arch> {
244 MOCK_ARCH.with(|arch| *arch.borrow())
245 }
246
247 fn set_mock_arch(arch: Option<Arch>) {
248 MOCK_ARCH.with(|mock| *mock.borrow_mut() = arch);
249 }
250
251 pub(crate) struct MockArchGuard {
252 previous: Option<Arch>,
253 }
254
255 impl MockArchGuard {
256 pub(crate) fn new(arch: Arch) -> Self {
257 let previous = get_mock_arch();
258 set_mock_arch(Some(arch));
259 Self { previous }
260 }
261 }
262
263 impl Drop for MockArchGuard {
264 fn drop(&mut self) {
265 set_mock_arch(self.previous);
266 }
267 }
268
269 pub(crate) fn run_with_arch<F, R>(arch: Arch, f: F) -> R
272 where
273 F: FnOnce() -> R,
274 {
275 let _guard = MockArchGuard::new(arch);
276 f()
277 }
278
279 pub(crate) fn x86_64() -> Arch {
280 Arch::new(target_lexicon::Architecture::X86_64, None)
281 }
282
283 pub(crate) fn aarch64() -> Arch {
284 Arch::new(
285 target_lexicon::Architecture::Aarch64(target_lexicon::Aarch64Architecture::Aarch64),
286 None,
287 )
288 }
289}