1use std::fmt;
2use std::str::FromStr;
3
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum OSKind {
6 Windows,
7 MacOS,
8 Linux,
9 FreeBSD,
10 OpenBSD,
11 NetBSD,
12 Android,
13 Ios,
14 Unknown,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub enum CpuArch {
19 X86,
20 X86_64,
21 Arm,
22 Aarch64,
23 Ppc,
24 Ppc64,
25 Riscv64,
26 S390x,
27 Unknown,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct ParseCpuArchError;
32
33impl fmt::Display for ParseCpuArchError {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 write!(f, "failed to parse CPU architecture")
36 }
37}
38
39impl std::error::Error for ParseCpuArchError {}
40
41impl FromStr for CpuArch {
42 type Err = ParseCpuArchError;
43
44 fn from_str(s: &str) -> Result<Self, Self::Err> {
45 match s {
46 "x86" => Ok(Self::X86),
47 "x86_64" => Ok(Self::X86_64),
48 "arm" => Ok(Self::Arm),
49 "aarch64" => Ok(Self::Aarch64),
50 "powerpc" => Ok(Self::Ppc),
51 "powerpc64" => Ok(Self::Ppc64),
52 "riscv64" => Ok(Self::Riscv64),
53 "s390x" => Ok(Self::S390x),
54 _ => Ok(Self::Unknown),
55 }
56 }
57}
58
59impl FromStr for OSKind {
60 type Err = ();
61
62 fn from_str(s: &str) -> Result<Self, Self::Err> {
63 match s {
64 "windows" => Ok(Self::Windows),
65 "macos" => Ok(Self::MacOS),
66 "linux" => Ok(Self::Linux),
67 "freebsd" => Ok(Self::FreeBSD),
68 "openbsd" => Ok(Self::OpenBSD),
69 "netbsd" => Ok(Self::NetBSD),
70 "android" => Ok(Self::Android),
71 "ios" => Ok(Self::Ios),
72 _ => Ok(Self::Unknown),
73 }
74 }
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct ArchitectureInfo {
79 pub is_os_64_bit: bool,
80 pub cpu_arch: CpuArch,
81 pub os_kind: OSKind,
82}
83
84impl ArchitectureInfo {
85 pub fn new_compiletime() -> Self {
86 let is_os_64_bit = cfg!(target_pointer_width = "64");
87
88 #[cfg(target_arch = "x86")]
89 let cpu_arch = CpuArch::X86;
90 #[cfg(target_arch = "x86_64")]
91 let cpu_arch = CpuArch::X86_64;
92 #[cfg(target_arch = "arm")]
93 let cpu_arch = CpuArch::Arm;
94 #[cfg(target_arch = "aarch64")]
95 let cpu_arch = CpuArch::Aarch64;
96 #[cfg(target_arch = "powerpc")]
97 let cpu_arch = CpuArch::Ppc;
98 #[cfg(target_arch = "powerpc64")]
99 let cpu_arch = CpuArch::Ppc64;
100 #[cfg(target_arch = "riscv64")]
101 let cpu_arch = CpuArch::Riscv64;
102 #[cfg(target_arch = "s390x")]
103 let cpu_arch = CpuArch::S390x;
104 #[cfg(not(any(
105 target_arch = "x86",
106 target_arch = "x86_64",
107 target_arch = "arm",
108 target_arch = "aarch64",
109 target_arch = "powerpc",
110 target_arch = "powerpc64",
111 target_arch = "riscv64",
112 target_arch = "s390x"
113 )))]
114 let cpu_arch = CpuArch::Unknown;
115
116 #[cfg(target_os = "windows")]
117 let os_kind = OSKind::Windows;
118 #[cfg(target_os = "macos")]
119 let os_kind = OSKind::MacOS;
120 #[cfg(target_os = "linux")]
121 let os_kind = OSKind::Linux;
122 #[cfg(target_os = "freebsd")]
123 let os_kind = OSKind::FreeBSD;
124 #[cfg(target_os = "openbsd")]
125 let os_kind = OSKind::OpenBSD;
126 #[cfg(target_os = "netbsd")]
127 let os_kind = OSKind::NetBSD;
128 #[cfg(target_os = "android")]
129 let os_kind = OSKind::Android;
130 #[cfg(target_os = "ios")]
131 let os_kind = OSKind::Ios;
132 #[cfg(not(any(
133 target_os = "windows",
134 target_os = "macos",
135 target_os = "linux",
136 target_os = "freebsd",
137 target_os = "openbsd",
138 target_os = "netbsd",
139 target_os = "android",
140 target_os = "ios"
141 )))]
142 let os_kind = OSKind::Unknown;
143
144 Self {
145 is_os_64_bit,
146 cpu_arch,
147 os_kind,
148 }
149 }
150
151 pub fn new() -> Self {
152 Self::new_compiletime()
153 }
154}
155
156impl Default for ArchitectureInfo {
157 fn default() -> Self {
158 Self::new()
159 }
160}
161
162pub fn format_arch(arch: &CpuArch) -> &str {
163 match arch {
164 CpuArch::X86 => "x86",
165 CpuArch::X86_64 => "x86_64",
166 CpuArch::Arm => "ARM",
167 CpuArch::Aarch64 => "ARM64",
168 CpuArch::Ppc => "PowerPC",
169 CpuArch::Ppc64 => "PowerPC64",
170 CpuArch::Riscv64 => "RISC-V 64",
171 CpuArch::S390x => "s390x",
172 CpuArch::Unknown => "Unknown",
173 }
174}
175
176pub fn format_os(os: &OSKind) -> &str {
177 match os {
178 OSKind::Windows => "Windows",
179 OSKind::MacOS => "macOS",
180 OSKind::Linux => "Linux",
181 OSKind::FreeBSD => "FreeBSD",
182 OSKind::OpenBSD => "OpenBSD",
183 OSKind::NetBSD => "NetBSD",
184 OSKind::Android => "Android",
185 OSKind::Ios => "iOS",
186 OSKind::Unknown => "Unknown OS",
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::{ArchitectureInfo, CpuArch, OSKind, format_arch, format_os};
193 use std::str::FromStr;
194
195 #[test]
196 fn cpu_arch_from_str_maps_known_and_unknown_values() {
197 assert_eq!(CpuArch::from_str("x86").expect("x86 parse"), CpuArch::X86);
198 assert_eq!(
199 CpuArch::from_str("aarch64").expect("aarch64 parse"),
200 CpuArch::Aarch64
201 );
202 assert_eq!(
203 CpuArch::from_str("something-else").expect("unknown parse"),
204 CpuArch::Unknown
205 );
206 }
207
208 #[test]
209 fn os_kind_from_str_maps_known_and_unknown_values() {
210 assert_eq!(
211 OSKind::from_str("windows").expect("windows parse"),
212 OSKind::Windows
213 );
214 assert_eq!(
215 OSKind::from_str("linux").expect("linux parse"),
216 OSKind::Linux
217 );
218 assert_eq!(
219 OSKind::from_str("weird-os").expect("unknown parse"),
220 OSKind::Unknown
221 );
222 }
223
224 #[test]
225 fn formatter_outputs_are_stable() {
226 assert_eq!(format_arch(&CpuArch::X86_64), "x86_64");
227 assert_eq!(format_arch(&CpuArch::Unknown), "Unknown");
228 assert_eq!(format_os(&OSKind::MacOS), "macOS");
229 assert_eq!(format_os(&OSKind::Unknown), "Unknown OS");
230 }
231
232 #[test]
233 fn architecture_info_default_constructs_reasonable_values() {
234 let info = ArchitectureInfo::default();
235 assert!(!format_arch(&info.cpu_arch).is_empty());
236 assert!(!format_os(&info.os_kind).is_empty());
237 }
238}