1use std::fmt;
8use std::str::FromStr;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum Target {
13 X86_64Linux,
15 Aarch64Linux,
17 Aarch64Macos,
19}
20
21impl Target {
22 #[cfg(all(target_arch = "x86_64", target_os = "linux"))]
27 pub fn host() -> Self {
28 Target::X86_64Linux
29 }
30
31 #[cfg(all(target_arch = "aarch64", target_os = "linux"))]
32 pub fn host() -> Self {
33 Target::Aarch64Linux
34 }
35
36 #[cfg(all(target_arch = "aarch64", target_os = "macos"))]
37 pub fn host() -> Self {
38 Target::Aarch64Macos
39 }
40
41 #[cfg(not(any(
42 all(target_arch = "x86_64", target_os = "linux"),
43 all(target_arch = "aarch64", target_os = "linux"),
44 all(target_arch = "aarch64", target_os = "macos")
45 )))]
46 pub fn host() -> Self {
47 Target::X86_64Linux
51 }
52
53 pub fn arch(&self) -> Arch {
55 match self {
56 Target::X86_64Linux => Arch::X86_64,
57 Target::Aarch64Linux | Target::Aarch64Macos => Arch::Aarch64,
58 }
59 }
60
61 pub fn os(&self) -> Os {
63 match self {
64 Target::X86_64Linux | Target::Aarch64Linux => Os::Linux,
65 Target::Aarch64Macos => Os::Macos,
66 }
67 }
68
69 pub fn elf_machine(&self) -> Option<u16> {
74 if !self.is_elf() {
75 return None;
76 }
77 match self.arch() {
78 Arch::X86_64 => Some(0x3E), Arch::Aarch64 => Some(0xB7), }
81 }
82
83 pub fn page_size(&self) -> u64 {
87 match self {
88 Target::X86_64Linux | Target::Aarch64Linux => 0x1000, Target::Aarch64Macos => 0x4000, }
93 }
94
95 pub fn default_base_addr(&self) -> u64 {
99 match self {
100 Target::X86_64Linux | Target::Aarch64Linux => 0x400000,
102 Target::Aarch64Macos => 0x100000000,
105 }
106 }
107
108 pub fn pointer_size(&self) -> u32 {
110 match self.arch() {
111 Arch::X86_64 | Arch::Aarch64 => 8, }
113 }
114
115 pub fn stack_alignment(&self) -> u32 {
119 match self {
120 Target::X86_64Linux | Target::Aarch64Linux | Target::Aarch64Macos => 16,
122 }
123 }
124
125 pub fn triple(&self) -> &'static str {
129 match self {
130 Target::X86_64Linux => "x86_64-unknown-linux-gnu",
131 Target::Aarch64Linux => "aarch64-unknown-linux-gnu",
132 Target::Aarch64Macos => "aarch64-apple-darwin",
133 }
134 }
135
136 pub fn is_macho(&self) -> bool {
138 matches!(self, Target::Aarch64Macos)
139 }
140
141 pub fn is_elf(&self) -> bool {
143 matches!(self, Target::X86_64Linux | Target::Aarch64Linux)
144 }
145
146 pub fn macos_min_version(&self) -> Option<u32> {
156 match self {
157 Target::Aarch64Macos => Some(0x000B0000), Target::X86_64Linux | Target::Aarch64Linux => None,
159 }
160 }
161
162 pub fn all() -> &'static [Target] {
164 &[
165 Target::X86_64Linux,
166 Target::Aarch64Linux,
167 Target::Aarch64Macos,
168 ]
169 }
170
171 pub fn all_names() -> &'static str {
173 "x86-64-linux, aarch64-linux, aarch64-macos"
174 }
175}
176
177impl fmt::Display for Target {
178 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179 match self {
180 Target::X86_64Linux => write!(f, "x86-64-linux"),
181 Target::Aarch64Linux => write!(f, "aarch64-linux"),
182 Target::Aarch64Macos => write!(f, "aarch64-macos"),
183 }
184 }
185}
186
187#[derive(Debug, Clone, PartialEq, Eq)]
189pub struct ParseTargetError {
190 input: String,
191}
192
193impl fmt::Display for ParseTargetError {
194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195 write!(
196 f,
197 "unknown target '{}'. Valid targets: {}",
198 self.input,
199 Target::all()
200 .iter()
201 .map(|t| t.to_string())
202 .collect::<Vec<_>>()
203 .join(", ")
204 )
205 }
206}
207
208impl std::error::Error for ParseTargetError {}
209
210impl Default for Target {
211 fn default() -> Self {
216 Self::host()
217 }
218}
219
220impl FromStr for Target {
221 type Err = ParseTargetError;
222
223 fn from_str(s: &str) -> Result<Self, Self::Err> {
224 match s {
225 "x86-64-linux" | "x86_64-linux" | "x86_64-unknown-linux-gnu" => Ok(Target::X86_64Linux),
226 "aarch64-linux" | "arm64-linux" | "aarch64-unknown-linux-gnu" => {
227 Ok(Target::Aarch64Linux)
228 }
229 "aarch64-macos" | "arm64-macos" | "aarch64-apple-darwin" => Ok(Target::Aarch64Macos),
230 _ => Err(ParseTargetError {
231 input: s.to_string(),
232 }),
233 }
234 }
235}
236
237#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
239pub enum Arch {
240 X86_64,
242 Aarch64,
244}
245
246impl fmt::Display for Arch {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 match self {
249 Arch::X86_64 => write!(f, "x86-64"),
250 Arch::Aarch64 => write!(f, "aarch64"),
251 }
252 }
253}
254
255#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
257pub enum Os {
258 Linux,
260 Macos,
262}
263
264impl fmt::Display for Os {
265 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266 match self {
267 Os::Linux => write!(f, "linux"),
268 Os::Macos => write!(f, "macos"),
269 }
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276
277 #[test]
278 fn test_target_parsing() {
279 assert_eq!(
280 "x86-64-linux".parse::<Target>().unwrap(),
281 Target::X86_64Linux
282 );
283 assert_eq!(
284 "x86_64-linux".parse::<Target>().unwrap(),
285 Target::X86_64Linux
286 );
287 assert_eq!(
288 "aarch64-linux".parse::<Target>().unwrap(),
289 Target::Aarch64Linux
290 );
291 assert_eq!(
292 "arm64-linux".parse::<Target>().unwrap(),
293 Target::Aarch64Linux
294 );
295 assert_eq!(
296 "aarch64-macos".parse::<Target>().unwrap(),
297 Target::Aarch64Macos
298 );
299 assert_eq!(
300 "arm64-macos".parse::<Target>().unwrap(),
301 Target::Aarch64Macos
302 );
303 assert_eq!(
304 "aarch64-apple-darwin".parse::<Target>().unwrap(),
305 Target::Aarch64Macos
306 );
307 }
308
309 #[test]
310 fn test_target_display() {
311 assert_eq!(Target::X86_64Linux.to_string(), "x86-64-linux");
312 assert_eq!(Target::Aarch64Linux.to_string(), "aarch64-linux");
313 assert_eq!(Target::Aarch64Macos.to_string(), "aarch64-macos");
314 }
315
316 #[test]
317 fn test_invalid_target() {
318 assert!("windows".parse::<Target>().is_err());
319 assert!("riscv64".parse::<Target>().is_err());
320 }
321
322 #[test]
323 fn test_elf_machine() {
324 assert_eq!(Target::X86_64Linux.elf_machine(), Some(0x3E));
325 assert_eq!(Target::Aarch64Linux.elf_machine(), Some(0xB7));
326 assert_eq!(Target::Aarch64Macos.elf_machine(), None);
328 }
329
330 #[test]
331 fn test_arch_decomposition() {
332 assert_eq!(Target::X86_64Linux.arch(), Arch::X86_64);
333 assert_eq!(Target::Aarch64Linux.arch(), Arch::Aarch64);
334 assert_eq!(Target::Aarch64Macos.arch(), Arch::Aarch64);
335 }
336
337 #[test]
338 fn test_os_decomposition() {
339 assert_eq!(Target::X86_64Linux.os(), Os::Linux);
340 assert_eq!(Target::Aarch64Linux.os(), Os::Linux);
341 assert_eq!(Target::Aarch64Macos.os(), Os::Macos);
342 }
343
344 #[test]
345 fn test_pointer_size() {
346 assert_eq!(Target::X86_64Linux.pointer_size(), 8);
347 assert_eq!(Target::Aarch64Linux.pointer_size(), 8);
348 assert_eq!(Target::Aarch64Macos.pointer_size(), 8);
349 }
350
351 #[test]
352 fn test_stack_alignment() {
353 assert_eq!(Target::X86_64Linux.stack_alignment(), 16);
354 assert_eq!(Target::Aarch64Linux.stack_alignment(), 16);
355 assert_eq!(Target::Aarch64Macos.stack_alignment(), 16);
356 }
357
358 #[test]
359 fn test_triple() {
360 assert_eq!(Target::X86_64Linux.triple(), "x86_64-unknown-linux-gnu");
361 assert_eq!(Target::Aarch64Linux.triple(), "aarch64-unknown-linux-gnu");
362 assert_eq!(Target::Aarch64Macos.triple(), "aarch64-apple-darwin");
363 }
364
365 #[test]
366 fn test_is_elf_macho() {
367 assert!(Target::X86_64Linux.is_elf());
368 assert!(Target::Aarch64Linux.is_elf());
369 assert!(!Target::Aarch64Macos.is_elf());
370
371 assert!(!Target::X86_64Linux.is_macho());
372 assert!(!Target::Aarch64Linux.is_macho());
373 assert!(Target::Aarch64Macos.is_macho());
374 }
375
376 #[test]
377 fn test_page_size() {
378 assert_eq!(Target::X86_64Linux.page_size(), 0x1000);
379 assert_eq!(Target::Aarch64Linux.page_size(), 0x1000);
380 assert_eq!(Target::Aarch64Macos.page_size(), 0x4000);
381 }
382
383 #[test]
384 fn test_macos_min_version() {
385 assert_eq!(Target::X86_64Linux.macos_min_version(), None);
387 assert_eq!(Target::Aarch64Linux.macos_min_version(), None);
388 assert_eq!(Target::Aarch64Macos.macos_min_version(), Some(0x000B0000));
390 }
391
392 #[test]
393 fn test_default_returns_host() {
394 assert_eq!(Target::default(), Target::host());
395 }
396
397 #[test]
398 fn test_display_from_str_round_trip() {
399 for target in Target::all() {
401 let displayed = target.to_string();
402 let parsed: Target = displayed
403 .parse()
404 .expect("Display output should be parseable");
405 assert_eq!(
406 *target, parsed,
407 "Round-trip failed for {}: displayed as '{}', parsed back as {:?}",
408 target, displayed, parsed
409 );
410 }
411 }
412}