Skip to main content

use_architecture/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::error::Error;
6
7/// CPU or target architecture vocabulary.
8#[allow(non_camel_case_types)]
9#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
10pub enum Architecture {
11    /// 32-bit x86 architecture.
12    X86,
13    /// 64-bit x86 architecture.
14    X86_64,
15    /// 32-bit ARM architecture.
16    Arm,
17    /// 64-bit ARM architecture.
18    Aarch64,
19    /// 32-bit RISC-V architecture.
20    RiscV32,
21    /// 64-bit RISC-V architecture.
22    RiscV64,
23    /// 32-bit WebAssembly architecture.
24    Wasm32,
25    /// 64-bit WebAssembly architecture.
26    Wasm64,
27    /// Unknown architecture.
28    Unknown,
29    /// A caller-defined architecture name.
30    Custom(String),
31}
32
33impl fmt::Display for Architecture {
34    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
35        match self {
36            Self::X86 => formatter.write_str("x86"),
37            Self::X86_64 => formatter.write_str("x86_64"),
38            Self::Arm => formatter.write_str("arm"),
39            Self::Aarch64 => formatter.write_str("aarch64"),
40            Self::RiscV32 => formatter.write_str("riscv32"),
41            Self::RiscV64 => formatter.write_str("riscv64"),
42            Self::Wasm32 => formatter.write_str("wasm32"),
43            Self::Wasm64 => formatter.write_str("wasm64"),
44            Self::Unknown => formatter.write_str("unknown"),
45            Self::Custom(value) => formatter.write_str(value),
46        }
47    }
48}
49
50impl FromStr for Architecture {
51    type Err = ArchitectureParseError;
52
53    fn from_str(value: &str) -> Result<Self, Self::Err> {
54        let trimmed = value.trim();
55
56        if trimmed.is_empty() {
57            return Err(ArchitectureParseError::Empty);
58        }
59
60        let key = trimmed.to_ascii_lowercase().replace(['_', '-', ' '], "");
61
62        match key.as_str() {
63            "x86" | "i386" | "i486" | "i586" | "i686" => Ok(Self::X86),
64            "x8664" | "amd64" | "x64" => Ok(Self::X86_64),
65            "arm" => Ok(Self::Arm),
66            "aarch64" | "arm64" => Ok(Self::Aarch64),
67            "riscv32" | "rv32" => Ok(Self::RiscV32),
68            "riscv64" | "rv64" => Ok(Self::RiscV64),
69            "wasm32" | "webassembly32" => Ok(Self::Wasm32),
70            "wasm64" | "webassembly64" => Ok(Self::Wasm64),
71            "unknown" => Ok(Self::Unknown),
72            _ => Ok(Self::Custom(trimmed.to_string())),
73        }
74    }
75}
76
77/// Error returned when parsing an architecture fails.
78#[derive(Clone, Copy, Debug, Eq, PartialEq)]
79pub enum ArchitectureParseError {
80    /// The architecture name was empty after trimming whitespace.
81    Empty,
82}
83
84impl fmt::Display for ArchitectureParseError {
85    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
86        match self {
87            Self::Empty => formatter.write_str("architecture cannot be empty"),
88        }
89    }
90}
91
92impl Error for ArchitectureParseError {}
93
94#[cfg(test)]
95mod tests {
96    use super::{Architecture, ArchitectureParseError};
97
98    #[test]
99    fn parses_known_architectures() -> Result<(), ArchitectureParseError> {
100        assert_eq!("x86".parse::<Architecture>()?, Architecture::X86);
101        assert_eq!("x86_64".parse::<Architecture>()?, Architecture::X86_64);
102        assert_eq!("arm".parse::<Architecture>()?, Architecture::Arm);
103        assert_eq!("aarch64".parse::<Architecture>()?, Architecture::Aarch64);
104        assert_eq!("riscv32".parse::<Architecture>()?, Architecture::RiscV32);
105        assert_eq!("riscv64".parse::<Architecture>()?, Architecture::RiscV64);
106        assert_eq!("wasm32".parse::<Architecture>()?, Architecture::Wasm32);
107        assert_eq!("wasm64".parse::<Architecture>()?, Architecture::Wasm64);
108        assert_eq!("unknown".parse::<Architecture>()?, Architecture::Unknown);
109        Ok(())
110    }
111
112    #[test]
113    fn parses_obvious_aliases() -> Result<(), ArchitectureParseError> {
114        assert_eq!("amd64".parse::<Architecture>()?, Architecture::X86_64);
115        assert_eq!("i686".parse::<Architecture>()?, Architecture::X86);
116        assert_eq!("arm64".parse::<Architecture>()?, Architecture::Aarch64);
117        assert_eq!("risc-v64".parse::<Architecture>()?, Architecture::RiscV64);
118        assert_eq!(
119            "webassembly32".parse::<Architecture>()?,
120            Architecture::Wasm32
121        );
122        Ok(())
123    }
124
125    #[test]
126    fn stores_custom_architectures() -> Result<(), ArchitectureParseError> {
127        assert_eq!(
128            "loongarch64".parse::<Architecture>()?,
129            Architecture::Custom("loongarch64".to_string())
130        );
131        Ok(())
132    }
133
134    #[test]
135    fn rejects_empty_architecture_names() {
136        assert_eq!(
137            "  ".parse::<Architecture>(),
138            Err(ArchitectureParseError::Empty)
139        );
140    }
141
142    #[test]
143    fn displays_canonical_names() {
144        assert_eq!(Architecture::X86_64.to_string(), "x86_64");
145        assert_eq!(Architecture::Aarch64.to_string(), "aarch64");
146        assert_eq!(Architecture::RiscV32.to_string(), "riscv32");
147    }
148}