1use crate::helpers::find_command_on_path;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use std::env::consts;
5use std::fmt;
6use std::process::Command;
7
8#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
10#[cfg_attr(feature = "schematic", derive(schematic::Schematic))]
11#[serde(rename_all = "lowercase")]
12pub enum SystemArch {
13 X86,
14 #[serde(alias = "x86_64")]
15 X64,
16 Arm,
17 #[serde(alias = "aarch64")]
18 Arm64,
19 #[serde(alias = "loongarch64")]
20 LongArm64,
21 M68k,
22 Mips,
23 Mips64,
24 Powerpc,
25 Powerpc64,
26 Riscv64,
27 S390x,
28 Sparc64,
29}
30
31impl SystemArch {
32 pub fn from_env() -> SystemArch {
34 serde_json::from_value(Value::String(consts::ARCH.to_owned()))
35 .expect("Unknown architecture!")
36 }
37
38 pub fn to_rust_arch(&self) -> String {
40 match self {
41 Self::X64 => "x86_64".into(),
42 Self::Arm64 => "aarch64".into(),
43 Self::LongArm64 => "loongarch64".into(),
44 _ => self.to_string(),
45 }
46 }
47}
48
49impl Default for SystemArch {
50 #[cfg(target_arch = "wasm32")]
51 fn default() -> Self {
52 SystemArch::X64
53 }
54
55 #[cfg(not(target_arch = "wasm32"))]
56 fn default() -> Self {
57 SystemArch::from_env()
58 }
59}
60
61impl fmt::Display for SystemArch {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 write!(f, "{}", format!("{self:?}").to_lowercase())
64 }
65}
66
67#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
69#[cfg_attr(feature = "schematic", derive(schematic::Schematic))]
70#[serde(rename_all = "lowercase")]
71pub enum SystemOS {
72 Android,
73 Dragonfly,
74 FreeBSD,
75 IOS,
76 Linux,
77 #[serde(alias = "mac")]
78 MacOS,
79 NetBSD,
80 OpenBSD,
81 Solaris,
82 Windows,
83}
84
85impl SystemOS {
86 pub fn from_env() -> SystemOS {
88 serde_json::from_value(Value::String(consts::OS.to_owned()))
89 .expect("Unknown operating system!")
90 }
91
92 pub fn for_native<'value, T: AsRef<str> + ?Sized>(
94 &self,
95 unix: &'value T,
96 windows: &'value T,
97 ) -> &'value str {
98 if self.is_windows() {
99 windows.as_ref()
100 } else {
101 unix.as_ref()
102 }
103 }
104
105 pub fn get_exe_name(&self, name: impl AsRef<str>) -> String {
108 self.get_file_name(name, "exe")
109 }
110
111 pub fn get_file_name(&self, name: impl AsRef<str>, windows_ext: impl AsRef<str>) -> String {
114 let name = name.as_ref();
115 let ext = windows_ext.as_ref();
116
117 if self.is_windows() && !name.ends_with(ext) {
118 format!("{name}.{ext}")
119 } else {
120 name.to_owned()
121 }
122 }
123
124 pub fn is_bsd(&self) -> bool {
126 matches!(
127 self,
128 Self::Dragonfly | Self::FreeBSD | Self::NetBSD | Self::OpenBSD
129 )
130 }
131
132 pub fn is_linux(&self) -> bool {
134 matches!(self, Self::Linux)
135 }
136
137 pub fn is_mac(&self) -> bool {
139 matches!(self, Self::MacOS)
140 }
141
142 pub fn is_unix(&self) -> bool {
144 self.is_bsd() || matches!(self, Self::Linux | Self::MacOS)
145 }
146
147 pub fn is_windows(&self) -> bool {
149 matches!(self, Self::Windows)
150 }
151
152 pub fn to_rust_os(&self) -> String {
154 self.to_string()
155 }
156}
157
158impl Default for SystemOS {
159 #[cfg(target_arch = "wasm32")]
160 fn default() -> Self {
161 SystemOS::Linux
162 }
163
164 #[cfg(not(target_arch = "wasm32"))]
165 fn default() -> Self {
166 SystemOS::from_env()
167 }
168}
169
170impl fmt::Display for SystemOS {
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 write!(f, "{}", format!("{self:?}").to_lowercase())
173 }
174}
175
176#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
178#[cfg_attr(feature = "schematic", derive(schematic::Schematic))]
179#[serde(rename_all = "lowercase")]
180pub enum SystemLibc {
181 Gnu,
182 Musl,
183 #[default]
184 Unknown,
185}
186
187impl SystemLibc {
188 pub fn detect(os: SystemOS) -> Self {
190 match os {
191 SystemOS::IOS | SystemOS::MacOS => Self::Gnu,
192 SystemOS::Windows => Self::Unknown,
193 _ => {
194 if Self::is_musl() {
195 Self::Musl
196 } else {
197 Self::Gnu
198 }
199 }
200 }
201 }
202
203 pub fn is_musl() -> bool {
207 let mut command = if let Some(ldd_path) = find_command_on_path("ldd") {
208 let mut cmd = Command::new(ldd_path);
209 cmd.arg("--version");
210 cmd
211 } else if let Some(uname_path) = find_command_on_path("uname") {
212 Command::new(uname_path)
213 } else {
214 return false;
215 };
216
217 if let Ok(result) = command.output() {
218 let output = if result.status.success() {
219 String::from_utf8_lossy(&result.stdout).to_lowercase()
220 } else {
221 String::from_utf8_lossy(&result.stderr).to_lowercase()
223 };
224
225 return output.contains("musl") || output.contains("alpine");
226 }
227
228 false
229 }
230}
231
232impl fmt::Display for SystemLibc {
233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234 write!(f, "{}", format!("{self:?}").to_lowercase())
235 }
236}