1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::error::Error;
6
7#[allow(non_camel_case_types)]
9#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
10pub enum Architecture {
11 X86,
13 X86_64,
15 Arm,
17 Aarch64,
19 RiscV32,
21 RiscV64,
23 Wasm32,
25 Wasm64,
27 Unknown,
29 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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
79pub enum ArchitectureParseError {
80 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}