Skip to main content

bare_types/sys/
arch.rs

1//! CPU architecture type for system information.
2//!
3//! This module provides a type-safe abstraction for CPU architectures,
4//! ensuring valid architecture identifiers and providing convenient constants
5//! for common architectures.
6//!
7//! # Supported Architectures
8//!
9//! This type supports all major CPU architectures:
10//!
11//! - **x86**: 32-bit x86 (i386, i686)
12//! - **`x86_64`**: 64-bit x86 (amd64)
13//! - **aarch64**: 64-bit ARM (arm64)
14//! - **arm**: 32-bit ARM
15//! - **riscv32**: 32-bit RISC-V
16//! - **riscv64**: 64-bit RISC-V
17//! - **wasm32**: 32-bit WebAssembly
18//! - **wasm64**: 64-bit WebAssembly
19//! - **mips**: 32-bit MIPS
20//! - **mips64**: 64-bit MIPS
21//! - **powerpc**: 32-bit PowerPC
22//! - **powerpc64**: 64-bit PowerPC
23//! - **s390x**: IBM System/390
24//!
25//! # Examples
26//!
27//! ```rust
28//! use bare_types::sys::Arch;
29//!
30//! // Get current architecture (compile-time constant)
31//! let arch = Arch::current();
32//!
33//! // Check architecture properties
34//! assert!(arch.is_64_bit() || arch.is_32_bit());
35//!
36//! // Parse from string
37//! let arch: Arch = "x86_64".parse()?;
38//! assert_eq!(arch, Arch::X86_64);
39//! # Ok::<(), bare_types::sys::ArchError>(())
40//! ```
41
42use core::fmt;
43use core::str::FromStr;
44
45#[cfg(feature = "serde")]
46use serde::{Deserialize, Serialize};
47
48#[cfg(feature = "arbitrary")]
49use arbitrary::Arbitrary;
50
51/// Error type for architecture parsing.
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
54#[non_exhaustive]
55pub enum ArchError {
56    /// Unknown architecture string
57    ///
58    /// The provided string does not match any known CPU architecture.
59    /// Supported architectures include: x86, `x86_64`, aarch64, arm, riscv32,
60    /// riscv64, wasm32, wasm64, mips, mips64, powerpc, powerpc64, s390x.
61    UnknownArchitecture,
62}
63
64impl fmt::Display for ArchError {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        match self {
67            Self::UnknownArchitecture => write!(f, "unknown architecture"),
68        }
69    }
70}
71
72#[cfg(feature = "std")]
73impl std::error::Error for ArchError {}
74
75/// CPU architecture type.
76///
77/// This enum provides type-safe CPU architecture identifiers.
78/// All variants are validated at construction time.
79///
80/// # Invariants
81///
82/// - All variants represent valid CPU architectures
83/// - Architecture information is determined at compile time
84///
85/// # Examples
86///
87/// ```rust
88/// use bare_types::sys::Arch;
89///
90/// // Use predefined constants
91/// let arch = Arch::X86_64;
92/// assert!(arch.is_64_bit());
93///
94/// // Get current architecture
95/// let current = Arch::current();
96/// println!("Running on: {}", current);
97///
98/// // Convert to string representation
99/// assert_eq!(Arch::AARCH64.as_str(), "aarch64");
100/// # Ok::<(), bare_types::sys::ArchError>(())
101/// ```
102#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
103#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
104#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
105#[non_exhaustive]
106pub enum Arch {
107    /// 32-bit x86 architecture (i386, i686)
108    X86,
109    /// 64-bit x86 architecture (amd64)
110    X86_64,
111    /// 64-bit ARM architecture (arm64)
112    AARCH64,
113    /// 32-bit ARM architecture
114    ARM,
115    /// 32-bit RISC-V architecture
116    RISCV32,
117    /// 64-bit RISC-V architecture
118    RISCV64,
119    /// 32-bit WebAssembly
120    WASM32,
121    /// 64-bit WebAssembly
122    WASM64,
123    /// 32-bit MIPS architecture
124    MIPS,
125    /// 64-bit MIPS architecture
126    MIPS64,
127    /// 32-bit PowerPC architecture
128    POWERPC,
129    /// 64-bit PowerPC architecture
130    POWERPC64,
131    /// IBM System/390 architecture
132    S390X,
133}
134
135impl Arch {
136    /// Returns the current architecture (compile-time constant).
137    ///
138    /// This method returns the architecture the code was compiled for,
139    /// not necessarily the architecture of the host system (especially
140    /// relevant for cross-compilation).
141    ///
142    /// # Examples
143    ///
144    /// ```rust
145    /// use bare_types::sys::Arch;
146    ///
147    /// let arch = Arch::current();
148    /// println!("Compiled for: {}", arch);
149    /// ```
150    #[must_use]
151    pub const fn current() -> Self {
152        #[cfg(target_arch = "x86")]
153        return Self::X86;
154        #[cfg(target_arch = "x86_64")]
155        return Self::X86_64;
156        #[cfg(target_arch = "aarch64")]
157        return Self::AARCH64;
158        #[cfg(target_arch = "arm")]
159        return Self::ARM;
160        #[cfg(target_arch = "riscv32")]
161        return Self::RISCV32;
162        #[cfg(target_arch = "riscv64")]
163        return Self::RISCV64;
164        #[cfg(target_arch = "wasm32")]
165        return Self::WASM32;
166        #[cfg(target_arch = "wasm64")]
167        return Self::WASM64;
168        #[cfg(target_arch = "mips")]
169        return Self::MIPS;
170        #[cfg(target_arch = "mips64")]
171        return Self::MIPS64;
172        #[cfg(target_arch = "powerpc")]
173        return Self::POWERPC;
174        #[cfg(target_arch = "powerpc64")]
175        return Self::POWERPC64;
176        #[cfg(target_arch = "s390x")]
177        return Self::S390X;
178        #[cfg(not(any(
179            target_arch = "x86",
180            target_arch = "x86_64",
181            target_arch = "aarch64",
182            target_arch = "arm",
183            target_arch = "riscv32",
184            target_arch = "riscv64",
185            target_arch = "wasm32",
186            target_arch = "wasm64",
187            target_arch = "mips",
188            target_arch = "mips64",
189            target_arch = "powerpc",
190            target_arch = "powerpc64",
191            target_arch = "s390x",
192        )))]
193        {
194            // For unknown architectures, we can't provide a constant
195            // This branch should ideally not be reached
196            panic!("unsupported target architecture")
197        }
198    }
199
200    /// Returns the string representation of this architecture.
201    ///
202    /// # Examples
203    ///
204    /// ```rust
205    /// use bare_types::sys::Arch;
206    ///
207    /// assert_eq!(Arch::X86_64.as_str(), "x86_64");
208    /// assert_eq!(Arch::AARCH64.as_str(), "aarch64");
209    /// assert_eq!(Arch::X86.as_str(), "x86");
210    /// ```
211    #[must_use]
212    pub const fn as_str(&self) -> &'static str {
213        match self {
214            Self::X86 => "x86",
215            Self::X86_64 => "x86_64",
216            Self::AARCH64 => "aarch64",
217            Self::ARM => "arm",
218            Self::RISCV32 => "riscv32",
219            Self::RISCV64 => "riscv64",
220            Self::WASM32 => "wasm32",
221            Self::WASM64 => "wasm64",
222            Self::MIPS => "mips",
223            Self::MIPS64 => "mips64",
224            Self::POWERPC => "powerpc",
225            Self::POWERPC64 => "powerpc64",
226            Self::S390X => "s390x",
227        }
228    }
229
230    /// Returns `true` if this is a 64-bit architecture.
231    ///
232    /// # Examples
233    ///
234    /// ```rust
235    /// use bare_types::sys::Arch;
236    ///
237    /// assert!(Arch::X86_64.is_64_bit());
238    /// assert!(Arch::AARCH64.is_64_bit());
239    /// assert!(!Arch::X86.is_64_bit());
240    /// assert!(!Arch::ARM.is_64_bit());
241    /// ```
242    #[must_use]
243    pub const fn is_64_bit(&self) -> bool {
244        matches!(
245            self,
246            Self::X86_64
247                | Self::AARCH64
248                | Self::RISCV64
249                | Self::WASM64
250                | Self::MIPS64
251                | Self::POWERPC64
252                | Self::S390X
253        )
254    }
255
256    /// Returns `true` if this is a 32-bit architecture.
257    ///
258    /// # Examples
259    ///
260    /// ```rust
261    /// use bare_types::sys::Arch;
262    ///
263    /// assert!(Arch::X86.is_32_bit());
264    /// assert!(Arch::ARM.is_32_bit());
265    /// assert!(!Arch::X86_64.is_32_bit());
266    /// assert!(!Arch::AARCH64.is_32_bit());
267    /// ```
268    #[must_use]
269    pub const fn is_32_bit(&self) -> bool {
270        matches!(
271            self,
272            Self::X86 | Self::ARM | Self::RISCV32 | Self::WASM32 | Self::MIPS | Self::POWERPC
273        )
274    }
275
276    /// Returns `true` if this is an ARM architecture.
277    ///
278    /// # Examples
279    ///
280    /// ```rust
281    /// use bare_types::sys::Arch;
282    ///
283    /// assert!(Arch::AARCH64.is_arm());
284    /// assert!(Arch::ARM.is_arm());
285    /// assert!(!Arch::X86_64.is_arm());
286    /// ```
287    #[must_use]
288    pub const fn is_arm(&self) -> bool {
289        matches!(self, Self::ARM | Self::AARCH64)
290    }
291
292    /// Returns `true` if this is an x86 architecture.
293    ///
294    /// # Examples
295    ///
296    /// ```rust
297    /// use bare_types::sys::Arch;
298    ///
299    /// assert!(Arch::X86.is_x86());
300    /// assert!(Arch::X86_64.is_x86());
301    /// assert!(!Arch::AARCH64.is_x86());
302    /// ```
303    #[must_use]
304    pub const fn is_x86(&self) -> bool {
305        matches!(self, Self::X86 | Self::X86_64)
306    }
307
308    /// Returns `true` if this is a RISC-V architecture.
309    ///
310    /// # Examples
311    ///
312    /// ```rust
313    /// use bare_types::sys::Arch;
314    ///
315    /// assert!(Arch::RISCV32.is_riscv());
316    /// assert!(Arch::RISCV64.is_riscv());
317    /// assert!(!Arch::X86_64.is_riscv());
318    /// ```
319    #[must_use]
320    pub const fn is_riscv(&self) -> bool {
321        matches!(self, Self::RISCV32 | Self::RISCV64)
322    }
323
324    /// Returns `true` if this is a WebAssembly architecture.
325    ///
326    /// # Examples
327    ///
328    /// ```rust
329    /// use bare_types::sys::Arch;
330    ///
331    /// assert!(Arch::WASM32.is_wasm());
332    /// assert!(Arch::WASM64.is_wasm());
333    /// assert!(!Arch::X86_64.is_wasm());
334    /// ```
335    #[must_use]
336    pub const fn is_wasm(&self) -> bool {
337        matches!(self, Self::WASM32 | Self::WASM64)
338    }
339
340    /// Returns `true` if this is a MIPS architecture.
341    ///
342    /// # Examples
343    ///
344    /// ```rust
345    /// use bare_types::sys::Arch;
346    ///
347    /// assert!(Arch::MIPS.is_mips());
348    /// assert!(Arch::MIPS64.is_mips());
349    /// assert!(!Arch::X86_64.is_mips());
350    /// ```
351    #[must_use]
352    pub const fn is_mips(&self) -> bool {
353        matches!(self, Self::MIPS | Self::MIPS64)
354    }
355
356    /// Returns `true` if this is a PowerPC architecture.
357    ///
358    /// # Examples
359    ///
360    /// ```rust
361    /// use bare_types::sys::Arch;
362    ///
363    /// assert!(Arch::POWERPC.is_powerpc());
364    /// assert!(Arch::POWERPC64.is_powerpc());
365    /// assert!(!Arch::X86_64.is_powerpc());
366    /// ```
367    #[must_use]
368    pub const fn is_powerpc(&self) -> bool {
369        matches!(self, Self::POWERPC | Self::POWERPC64)
370    }
371
372    /// Returns the pointer width in bits for this architecture.
373    ///
374    /// # Examples
375    ///
376    /// ```rust
377    /// use bare_types::sys::Arch;
378    ///
379    /// assert_eq!(Arch::X86_64.pointer_width(), 64);
380    /// assert_eq!(Arch::X86.pointer_width(), 32);
381    /// assert_eq!(Arch::AARCH64.pointer_width(), 64);
382    /// ```
383    #[must_use]
384    pub const fn pointer_width(&self) -> u8 {
385        if self.is_64_bit() { 64 } else { 32 }
386    }
387}
388
389impl TryFrom<&str> for Arch {
390    type Error = ArchError;
391
392    fn try_from(s: &str) -> Result<Self, Self::Error> {
393        s.parse()
394    }
395}
396
397impl FromStr for Arch {
398    type Err = ArchError;
399
400    fn from_str(s: &str) -> Result<Self, Self::Err> {
401        match s.to_lowercase().as_str() {
402            "x86" | "i386" | "i486" | "i586" | "i686" => Ok(Self::X86),
403            "x86_64" | "amd64" | "x64" => Ok(Self::X86_64),
404            "aarch64" | "arm64" => Ok(Self::AARCH64),
405            "arm" => Ok(Self::ARM),
406            "riscv32" | "riscv-32" | "riscv_32" => Ok(Self::RISCV32),
407            "riscv64" | "riscv-64" | "riscv_64" => Ok(Self::RISCV64),
408            "wasm32" | "wasm-32" | "wasm_32" => Ok(Self::WASM32),
409            "wasm64" | "wasm-64" | "wasm_64" => Ok(Self::WASM64),
410            "mips" => Ok(Self::MIPS),
411            "mips64" => Ok(Self::MIPS64),
412            "powerpc" | "ppc" => Ok(Self::POWERPC),
413            "powerpc64" | "ppc64" => Ok(Self::POWERPC64),
414            "s390x" | "s390" => Ok(Self::S390X),
415            _ => Err(ArchError::UnknownArchitecture),
416        }
417    }
418}
419
420impl fmt::Display for Arch {
421    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
422        write!(f, "{}", self.as_str())
423    }
424}
425
426#[cfg(test)]
427mod tests {
428    use super::*;
429
430    #[test]
431    fn test_as_str() {
432        assert_eq!(Arch::X86.as_str(), "x86");
433        assert_eq!(Arch::X86_64.as_str(), "x86_64");
434        assert_eq!(Arch::AARCH64.as_str(), "aarch64");
435        assert_eq!(Arch::ARM.as_str(), "arm");
436        assert_eq!(Arch::RISCV32.as_str(), "riscv32");
437        assert_eq!(Arch::RISCV64.as_str(), "riscv64");
438        assert_eq!(Arch::WASM32.as_str(), "wasm32");
439        assert_eq!(Arch::WASM64.as_str(), "wasm64");
440        assert_eq!(Arch::MIPS.as_str(), "mips");
441        assert_eq!(Arch::MIPS64.as_str(), "mips64");
442        assert_eq!(Arch::POWERPC.as_str(), "powerpc");
443        assert_eq!(Arch::POWERPC64.as_str(), "powerpc64");
444        assert_eq!(Arch::S390X.as_str(), "s390x");
445    }
446
447    #[test]
448    fn test_is_64_bit() {
449        assert!(!Arch::X86.is_64_bit());
450        assert!(Arch::X86_64.is_64_bit());
451        assert!(Arch::AARCH64.is_64_bit());
452        assert!(!Arch::ARM.is_64_bit());
453        assert!(!Arch::RISCV32.is_64_bit());
454        assert!(Arch::RISCV64.is_64_bit());
455        assert!(!Arch::WASM32.is_64_bit());
456        assert!(Arch::WASM64.is_64_bit());
457        assert!(!Arch::MIPS.is_64_bit());
458        assert!(Arch::MIPS64.is_64_bit());
459        assert!(!Arch::POWERPC.is_64_bit());
460        assert!(Arch::POWERPC64.is_64_bit());
461        assert!(Arch::S390X.is_64_bit());
462    }
463
464    #[test]
465    fn test_is_32_bit() {
466        assert!(Arch::X86.is_32_bit());
467        assert!(!Arch::X86_64.is_32_bit());
468        assert!(!Arch::AARCH64.is_32_bit());
469        assert!(Arch::ARM.is_32_bit());
470        assert!(Arch::RISCV32.is_32_bit());
471        assert!(!Arch::RISCV64.is_32_bit());
472        assert!(Arch::WASM32.is_32_bit());
473        assert!(!Arch::WASM64.is_32_bit());
474        assert!(Arch::MIPS.is_32_bit());
475        assert!(!Arch::MIPS64.is_32_bit());
476        assert!(Arch::POWERPC.is_32_bit());
477        assert!(!Arch::POWERPC64.is_32_bit());
478        assert!(!Arch::S390X.is_32_bit());
479    }
480
481    #[test]
482    fn test_is_arm() {
483        assert!(!Arch::X86.is_arm());
484        assert!(!Arch::X86_64.is_arm());
485        assert!(Arch::AARCH64.is_arm());
486        assert!(Arch::ARM.is_arm());
487    }
488
489    #[test]
490    fn test_is_x86() {
491        assert!(Arch::X86.is_x86());
492        assert!(Arch::X86_64.is_x86());
493        assert!(!Arch::AARCH64.is_x86());
494        assert!(!Arch::ARM.is_x86());
495    }
496
497    #[test]
498    fn test_is_riscv() {
499        assert!(Arch::RISCV32.is_riscv());
500        assert!(Arch::RISCV64.is_riscv());
501        assert!(!Arch::X86_64.is_riscv());
502    }
503
504    #[test]
505    fn test_is_wasm() {
506        assert!(Arch::WASM32.is_wasm());
507        assert!(Arch::WASM64.is_wasm());
508        assert!(!Arch::X86_64.is_wasm());
509    }
510
511    #[test]
512    fn test_is_mips() {
513        assert!(Arch::MIPS.is_mips());
514        assert!(Arch::MIPS64.is_mips());
515        assert!(!Arch::X86_64.is_mips());
516    }
517
518    #[test]
519    fn test_is_powerpc() {
520        assert!(Arch::POWERPC.is_powerpc());
521        assert!(Arch::POWERPC64.is_powerpc());
522        assert!(!Arch::X86_64.is_powerpc());
523    }
524
525    #[test]
526    fn test_pointer_width() {
527        assert_eq!(Arch::X86.pointer_width(), 32);
528        assert_eq!(Arch::X86_64.pointer_width(), 64);
529        assert_eq!(Arch::AARCH64.pointer_width(), 64);
530        assert_eq!(Arch::ARM.pointer_width(), 32);
531    }
532
533    #[test]
534    fn test_from_str() {
535        assert_eq!("x86".parse::<Arch>().unwrap(), Arch::X86);
536        assert_eq!("x86_64".parse::<Arch>().unwrap(), Arch::X86_64);
537        assert_eq!("amd64".parse::<Arch>().unwrap(), Arch::X86_64);
538        assert_eq!("aarch64".parse::<Arch>().unwrap(), Arch::AARCH64);
539        assert_eq!("arm64".parse::<Arch>().unwrap(), Arch::AARCH64);
540        assert_eq!("arm".parse::<Arch>().unwrap(), Arch::ARM);
541        assert_eq!("riscv32".parse::<Arch>().unwrap(), Arch::RISCV32);
542        assert_eq!("riscv64".parse::<Arch>().unwrap(), Arch::RISCV64);
543        assert_eq!("wasm32".parse::<Arch>().unwrap(), Arch::WASM32);
544        assert_eq!("wasm64".parse::<Arch>().unwrap(), Arch::WASM64);
545        assert_eq!("mips".parse::<Arch>().unwrap(), Arch::MIPS);
546        assert_eq!("mips64".parse::<Arch>().unwrap(), Arch::MIPS64);
547        assert_eq!("powerpc".parse::<Arch>().unwrap(), Arch::POWERPC);
548        assert_eq!("ppc".parse::<Arch>().unwrap(), Arch::POWERPC);
549        assert_eq!("powerpc64".parse::<Arch>().unwrap(), Arch::POWERPC64);
550        assert_eq!("ppc64".parse::<Arch>().unwrap(), Arch::POWERPC64);
551        assert_eq!("s390x".parse::<Arch>().unwrap(), Arch::S390X);
552
553        // Test case insensitivity
554        assert_eq!("X86_64".parse::<Arch>().unwrap(), Arch::X86_64);
555        assert_eq!("AARCH64".parse::<Arch>().unwrap(), Arch::AARCH64);
556    }
557
558    #[test]
559    fn test_from_str_error() {
560        assert!("unknown".parse::<Arch>().is_err());
561        assert!("".parse::<Arch>().is_err());
562    }
563
564    #[test]
565    fn test_display() {
566        assert_eq!(format!("{}", Arch::X86_64), "x86_64");
567        assert_eq!(format!("{}", Arch::AARCH64), "aarch64");
568        assert_eq!(format!("{}", Arch::X86), "x86");
569    }
570
571    #[test]
572    fn test_current() {
573        // Just verify it doesn't panic
574        let _arch = Arch::current();
575    }
576
577    #[test]
578    fn test_copy() {
579        let arch = Arch::X86_64;
580        let arch2 = arch;
581        assert_eq!(arch, arch2);
582    }
583
584    #[test]
585    fn test_clone() {
586        let arch = Arch::X86_64;
587        let arch2 = arch.clone();
588        assert_eq!(arch, arch2);
589    }
590
591    #[test]
592    fn test_equality() {
593        assert_eq!(Arch::X86_64, Arch::X86_64);
594        assert_ne!(Arch::X86_64, Arch::X86);
595    }
596
597    #[test]
598    fn test_ordering() {
599        assert!(Arch::X86 < Arch::X86_64);
600        assert!(Arch::AARCH64 < Arch::ARM); // AARCH64 defined before ARM in enum
601    }
602}