1use std::fmt;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum Platform {
6 Linux,
7 MacOS,
8 Windows,
9 Unknown,
10}
11
12impl Platform {
13 pub fn detect() -> Self {
15 #[cfg(target_os = "linux")]
16 return Platform::Linux;
17
18 #[cfg(target_os = "macos")]
19 return Platform::MacOS;
20
21 #[cfg(target_os = "windows")]
22 return Platform::Windows;
23
24 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
25 return Platform::Unknown;
26 }
27
28 pub fn name(&self) -> &'static str {
30 match self {
31 Platform::Linux => "Linux",
32 Platform::MacOS => "macOS",
33 Platform::Windows => "Windows",
34 Platform::Unknown => "Unknown",
35 }
36 }
37
38 pub fn exe_extension(&self) -> &'static str {
40 match self {
41 Platform::Windows => ".exe",
42 _ => "",
43 }
44 }
45
46 pub fn dylib_extension(&self) -> &'static str {
48 match self {
49 Platform::Linux => ".so",
50 Platform::MacOS => ".dylib",
51 Platform::Windows => ".dll",
52 Platform::Unknown => "",
53 }
54 }
55
56 pub fn staticlib_extension(&self) -> &'static str {
58 match self {
59 Platform::Windows => ".lib",
60 _ => ".a",
61 }
62 }
63
64 pub fn is_unix(&self) -> bool {
66 matches!(self, Platform::Linux | Platform::MacOS)
67 }
68
69 pub fn shell_command(&self) -> (&'static str, &'static [&'static str]) {
71 match self {
72 Platform::Windows => ("cmd", &["/C"]),
73 _ => ("sh", &["-c"]),
74 }
75 }
76
77 pub fn package_manager(&self) -> Option<&'static str> {
79 match self {
80 Platform::Linux => {
81 if std::path::Path::new("/etc/debian_version").exists() {
83 Some("apt")
84 } else if std::path::Path::new("/etc/redhat-release").exists() {
85 Some("yum")
86 } else if std::path::Path::new("/etc/arch-release").exists() {
87 Some("pacman")
88 } else if std::path::Path::new("/etc/alpine-release").exists() {
89 Some("apk")
90 } else {
91 None
92 }
93 },
94 Platform::MacOS => Some("brew"),
95 Platform::Windows => Some("winget"),
96 Platform::Unknown => None,
97 }
98 }
99
100 pub fn common_paths(&self) -> CommonPaths {
102 match self {
103 Platform::Linux => CommonPaths {
104 config: "~/.config",
105 cache: "~/.cache",
106 data: "~/.local/share",
107 temp: "/tmp",
108 },
109 Platform::MacOS => CommonPaths {
110 config: "~/Library/Application Support",
111 cache: "~/Library/Caches",
112 data: "~/Library/Application Support",
113 temp: "/tmp",
114 },
115 Platform::Windows => CommonPaths {
116 config: "%APPDATA%",
117 cache: "%LOCALAPPDATA%",
118 data: "%APPDATA%",
119 temp: "%TEMP%",
120 },
121 Platform::Unknown => CommonPaths {
122 config: "~/.config",
123 cache: "~/.cache",
124 data: "~/.local/share",
125 temp: "/tmp",
126 },
127 }
128 }
129}
130
131impl fmt::Display for Platform {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 write!(f, "{}", self.name())
134 }
135}
136
137#[derive(Debug, Clone)]
139pub struct CommonPaths {
140 pub config: &'static str,
141 pub cache: &'static str,
142 pub data: &'static str,
143 pub temp: &'static str,
144}
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq)]
148pub enum Architecture {
149 X86,
150 X64,
151 Arm,
152 Arm64,
153 Unknown,
154}
155
156impl Architecture {
157 pub fn detect() -> Self {
159 #[cfg(target_arch = "x86")]
160 return Architecture::X86;
161
162 #[cfg(target_arch = "x86_64")]
163 return Architecture::X64;
164
165 #[cfg(target_arch = "arm")]
166 return Architecture::Arm;
167
168 #[cfg(target_arch = "aarch64")]
169 return Architecture::Arm64;
170
171 #[cfg(not(any(
172 target_arch = "x86",
173 target_arch = "x86_64",
174 target_arch = "arm",
175 target_arch = "aarch64"
176 )))]
177 return Architecture::Unknown;
178 }
179
180 pub fn name(&self) -> &'static str {
182 match self {
183 Architecture::X86 => "x86",
184 Architecture::X64 => "x64",
185 Architecture::Arm => "arm",
186 Architecture::Arm64 => "arm64",
187 Architecture::Unknown => "unknown",
188 }
189 }
190
191 pub fn is_64bit(&self) -> bool {
193 matches!(self, Architecture::X64 | Architecture::Arm64)
194 }
195}
196
197impl fmt::Display for Architecture {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 write!(f, "{}", self.name())
200 }
201}
202
203pub struct PlatformInfo {
205 pub platform: Platform,
206 pub architecture: Architecture,
207 pub hostname: Option<String>,
208 pub username: Option<String>,
209}
210
211impl PlatformInfo {
212 pub fn gather() -> Self {
214 Self {
215 platform: Platform::detect(),
216 architecture: Architecture::detect(),
217 hostname: hostname::get().ok().and_then(|h| h.into_string().ok()),
218 username: std::env::var("USER")
219 .or_else(|_| std::env::var("USERNAME"))
220 .ok(),
221 }
222 }
223}
224
225impl fmt::Display for PlatformInfo {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 write!(
228 f,
229 "{} {} (host: {}, user: {})",
230 self.platform,
231 self.architecture,
232 self.hostname.as_deref().unwrap_or("unknown"),
233 self.username.as_deref().unwrap_or("unknown")
234 )
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 #[test]
243 fn test_platform_detection() {
244 let platform = Platform::detect();
245 assert_ne!(platform, Platform::Unknown);
246 }
247
248 #[test]
249 fn test_architecture_detection() {
250 let arch = Architecture::detect();
251 assert_ne!(arch, Architecture::Unknown);
252 }
253
254 #[test]
255 fn test_platform_info() {
256 let info = PlatformInfo::gather();
257 println!("Platform info: {}", info);
258 }
259}