1use std::process::Command;
43use serde::{Serialize, Deserialize};
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
48pub enum PlatformType {
49 Linux,
51 MacOS,
53 Windows,
55 WebAssembly,
57 Unknown,
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
63#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
64pub enum Architecture {
65 X86_64,
67 AArch64,
69 X86,
71 Arm,
73 Wasm,
75 Unknown,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
81#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
82pub struct PlatformInfo {
83 pub platform_type: PlatformType,
85 pub os_name: String,
87 pub os_version: String,
89 pub architecture: Architecture,
91 pub cpu_cores: usize,
93 pub total_memory: u64,
95 pub in_container: bool,
97 pub in_vm: bool,
99 pub available_strategies: Vec<DiscoveryStrategy>,
101}
102
103#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
105#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
106pub enum DiscoveryStrategy {
107 ProcFs,
109 SysFs,
111 DBus,
113 UDev,
115 IOKit,
117 WMI,
119 Registry,
121 SystemCalls,
123 NetworkScan,
125 VirtualFS,
127 MockFallback,
129}
130
131impl PlatformInfo {
132 pub fn new() -> Self {
134 let platform_info = detect_platform();
135 platform_info
136 }
137
138 pub fn has_strategy(&self, strategy: &DiscoveryStrategy) -> bool {
140 self.available_strategies.contains(strategy)
141 }
142
143 pub fn best_hardware_strategy(&self) -> DiscoveryStrategy {
145 match self.platform_type {
146 PlatformType::Linux => {
147 if self.has_strategy(&DiscoveryStrategy::SysFs) {
148 return DiscoveryStrategy::SysFs;
149 }
150 if self.has_strategy(&DiscoveryStrategy::ProcFs) {
151 return DiscoveryStrategy::ProcFs;
152 }
153 if self.has_strategy(&DiscoveryStrategy::UDev) {
154 return DiscoveryStrategy::UDev;
155 }
156 DiscoveryStrategy::MockFallback
157 }
158 PlatformType::MacOS => {
159 if self.has_strategy(&DiscoveryStrategy::IOKit) {
160 return DiscoveryStrategy::IOKit;
161 }
162 DiscoveryStrategy::SystemCalls
163 }
164 PlatformType::Windows => {
165 if self.has_strategy(&DiscoveryStrategy::WMI) {
166 return DiscoveryStrategy::WMI;
167 }
168 DiscoveryStrategy::SystemCalls
169 }
170 PlatformType::WebAssembly => DiscoveryStrategy::VirtualFS,
171 PlatformType::Unknown => DiscoveryStrategy::MockFallback,
172 }
173 }
174
175 pub fn best_network_strategy(&self) -> DiscoveryStrategy {
177 match self.platform_type {
178 PlatformType::Linux => {
179 if self.has_strategy(&DiscoveryStrategy::NetworkScan) {
180 return DiscoveryStrategy::NetworkScan;
181 }
182 DiscoveryStrategy::SysFs
183 }
184 PlatformType::MacOS => DiscoveryStrategy::SystemCalls,
185 PlatformType::Windows => DiscoveryStrategy::WMI,
186 PlatformType::WebAssembly => DiscoveryStrategy::VirtualFS,
187 PlatformType::Unknown => DiscoveryStrategy::MockFallback,
188 }
189 }
190}
191
192impl Default for PlatformInfo {
193 fn default() -> Self {
194 Self::new()
195 }
196}
197
198pub fn get_platform_info() -> PlatformInfo {
200 detect_platform()
201}
202
203fn detect_platform() -> PlatformInfo {
205 let (os_name, os_version) = detect_os();
206 let architecture = detect_architecture();
207 let cpu_cores = detect_cpu_cores();
208 let total_memory = detect_total_memory();
209 let in_container = detect_container();
210 let in_vm = detect_virtual_machine();
211 let platform_type = detect_platform_type(&os_name);
212 let available_strategies = detect_available_strategies(&platform_type);
213
214 PlatformInfo {
215 platform_type,
216 os_name,
217 os_version,
218 architecture,
219 cpu_cores,
220 total_memory,
221 in_container,
222 in_vm,
223 available_strategies,
224 }
225}
226
227fn detect_os() -> (String, String) {
229 let os = std::env::consts::OS;
230 let version = std::env::consts::FAMILY;
231
232 match os {
233 "linux" => {
234 let version_info = read_os_release();
235 let name = version_info.get("NAME").cloned().unwrap_or_else(|| "Linux".to_string());
236 let version_id = version_info.get("VERSION_ID")
237 .or(version_info.get("VERSION"))
238 .cloned()
239 .unwrap_or_else(|| "unknown".to_string());
240 (name, version_id)
241 }
242 "macos" => {
243 let version = detect_macos_version();
244 ("macOS".to_string(), version)
245 }
246 "windows" => {
247 let version = detect_windows_version();
248 ("Windows".to_string(), version)
249 }
250 _ => (os.to_string(), version.to_string()),
251 }
252}
253
254fn read_os_release() -> HashMap<String, String> {
256 let mut result = HashMap::new();
257
258 if let Ok(content) = std::fs::read_to_string("/etc/os-release") {
259 for line in content.lines() {
260 if let Some(eq_pos) = line.find('=') {
261 let key = line[..eq_pos].to_string();
262 let value = line[eq_pos + 1..].trim_matches('"').to_string();
263 result.insert(key, value);
264 }
265 }
266 }
267
268 result
269}
270
271fn detect_macos_version() -> String {
273 let output = Command::new("sw_vers")
274 .arg("-productVersion")
275 .output();
276
277 match output {
278 Ok(output) => {
279 if output.status.success() {
280 if let Ok(version) = String::from_utf8(output.stdout) {
281 return version.trim().to_string();
282 }
283 }
284 }
285 Err(_) => {}
286 }
287
288 "unknown".to_string()
289}
290
291fn detect_windows_version() -> String {
293 let output = Command::new("cmd")
294 .args(&["/c", "ver"])
295 .output();
296
297 match output {
298 Ok(output) => {
299 if output.status.success() {
300 if let Ok(version) = String::from_utf8(output.stdout) {
301 return version.trim().to_string();
302 }
303 }
304 }
305 Err(_) => {}
306 }
307
308 "unknown".to_string()
309}
310
311fn detect_architecture() -> Architecture {
313 let arch = std::env::consts::ARCH;
314
315 match arch {
316 "x86_64" => Architecture::X86_64,
317 "aarch64" | "arm64" => Architecture::AArch64,
318 "x86" | "i686" | "i386" => Architecture::X86,
319 "arm" | "thumbv7" => Architecture::Arm,
320 "wasm32" => Architecture::Wasm,
321 _ => Architecture::Unknown,
322 }
323}
324
325fn detect_cpu_cores() -> usize {
327 std::thread::available_parallelism()
328 .map(|n| n.get())
329 .unwrap_or(1)
330}
331
332fn detect_total_memory() -> u64 {
334 if let Ok(content) = std::fs::read_to_string("/proc/meminfo") {
335 for line in content.lines() {
336 if line.starts_with("MemTotal:") {
337 if let Some(kb_str) = line.split_whitespace().nth(1) {
338 if let Ok(kb) = kb_str.parse::<u64>() {
339 return kb * 1024;
340 }
341 }
342 }
343 }
344 }
345 4 * 1024 * 1024 * 1024 }
347
348fn detect_container() -> bool {
350 if std::env::var("DOCKER_CONTAINER").is_ok() {
354 return true;
355 }
356
357 if std::path::Path::new("/.dockerenv").exists() {
359 return true;
360 }
361
362 if let Ok(content) = std::fs::read_to_string("/proc/1/cgroup") {
364 if content.contains("docker") || content.contains("containerd") {
365 return true;
366 }
367 }
368
369 if std::env::var("KUBERNETES_SERVICE_HOST").is_ok() {
371 return true;
372 }
373
374 false
375}
376
377fn detect_virtual_machine() -> bool {
379 if let Ok(content) = std::fs::read_to_string("/sys/hypervisor/type") {
381 if content.trim() == "xen" || content.contains("vmware") || content.contains("kvm") {
382 return true;
383 }
384 }
385
386 if std::path::Path::new("/sys/class/dmi/id/sys_vendor").exists() {
388 if let Ok(content) = std::fs::read_to_string("/sys/class/dmi/id/sys_vendor") {
389 let vendor = content.to_lowercase();
390 if vendor.contains("vmware") || vendor.contains("virtualbox") ||
391 vendor.contains("qemu") || vendor.contains("hyper-v") {
392 return true;
393 }
394 }
395 }
396
397 false
398}
399
400fn detect_platform_type(os_name: &str) -> PlatformType {
402 let os_lower = os_name.to_lowercase();
403
404 if os_lower.contains("linux") || os_lower.contains("ubuntu") ||
405 os_lower.contains("debian") || os_lower.contains("centos") ||
406 os_lower.contains("fedora") || os_lower.contains("rhel") ||
407 os_lower.contains("alpine") {
408 return PlatformType::Linux;
409 }
410
411 if os_lower.contains("mac") || os_lower.contains("darwin") {
412 return PlatformType::MacOS;
413 }
414
415 if os_lower.contains("windows") {
416 return PlatformType::Windows;
417 }
418
419 if std::env::consts::FAMILY == "wasm" {
420 return PlatformType::WebAssembly;
421 }
422
423 PlatformType::Unknown
424}
425
426fn detect_available_strategies(platform_type: &PlatformType) -> Vec<DiscoveryStrategy> {
428 let mut strategies = Vec::new();
429
430 match platform_type {
431 PlatformType::Linux => {
432 if std::path::Path::new("/proc").exists() {
433 strategies.push(DiscoveryStrategy::ProcFs);
434 }
435 if std::path::Path::new("/sys").exists() {
436 strategies.push(DiscoveryStrategy::SysFs);
437 }
438 if Command::new("dbus-send").output().is_ok() {
439 strategies.push(DiscoveryStrategy::DBus);
440 }
441 if std::path::Path::new("/dev").exists() {
442 strategies.push(DiscoveryStrategy::UDev);
443 }
444 strategies.push(DiscoveryStrategy::NetworkScan);
445 strategies.push(DiscoveryStrategy::MockFallback);
446 }
447 PlatformType::MacOS => {
448 strategies.push(DiscoveryStrategy::IOKit);
449 strategies.push(DiscoveryStrategy::SystemCalls);
450 strategies.push(DiscoveryStrategy::NetworkScan);
451 strategies.push(DiscoveryStrategy::MockFallback);
452 }
453 PlatformType::Windows => {
454 if Command::new("wmic").output().is_ok() {
455 strategies.push(DiscoveryStrategy::WMI);
456 }
457 strategies.push(DiscoveryStrategy::Registry);
458 strategies.push(DiscoveryStrategy::SystemCalls);
459 strategies.push(DiscoveryStrategy::NetworkScan);
460 strategies.push(DiscoveryStrategy::MockFallback);
461 }
462 PlatformType::WebAssembly => {
463 strategies.push(DiscoveryStrategy::VirtualFS);
464 strategies.push(DiscoveryStrategy::MockFallback);
465 }
466 PlatformType::Unknown => {
467 strategies.push(DiscoveryStrategy::SystemCalls);
468 strategies.push(DiscoveryStrategy::MockFallback);
469 }
470 }
471
472 strategies
473}
474
475#[derive(Debug, Clone, Serialize, Deserialize)]
477#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
478pub struct PlatformCompatibility {
479 pub is_fully_supported: bool,
481 pub platform_type: PlatformType,
483 pub supported_hardware: Vec<HardwareCategory>,
485 pub recommended_strategies: Vec<DiscoveryStrategy>,
487 pub limitations: Vec<String>,
489}
490
491#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
493#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
494pub enum HardwareCategory {
495 CPU,
497 GPU,
499 Memory,
501 Storage,
503 Network,
505 USB,
507 PCI,
509 Input,
511 Display,
513 Bluetooth,
515 Other,
517}
518
519impl PlatformCompatibility {
520 pub fn current() -> Self {
522 let platform = get_platform_info();
523 Self::from_platform(&platform)
524 }
525
526 pub fn from_platform(platform: &PlatformInfo) -> Self {
528 let mut supported_hardware = Vec::new();
529 let mut limitations = Vec::new();
530
531 supported_hardware.extend(vec![
532 HardwareCategory::CPU,
533 HardwareCategory::Memory,
534 HardwareCategory::Storage,
535 ]);
536
537 let recommended_strategies = match platform.platform_type {
538 PlatformType::Linux => {
539 supported_hardware.extend(vec![
540 HardwareCategory::GPU,
541 HardwareCategory::Network,
542 HardwareCategory::USB,
543 HardwareCategory::PCI,
544 HardwareCategory::Input,
545 HardwareCategory::Display,
546 HardwareCategory::Bluetooth,
547 ]);
548 vec![
549 DiscoveryStrategy::SysFs,
550 DiscoveryStrategy::ProcFs,
551 DiscoveryStrategy::UDev,
552 DiscoveryStrategy::NetworkScan,
553 ]
554 }
555 PlatformType::MacOS => {
556 supported_hardware.extend(vec![
557 HardwareCategory::GPU,
558 HardwareCategory::Network,
559 HardwareCategory::USB,
560 HardwareCategory::PCI,
561 HardwareCategory::Input,
562 HardwareCategory::Display,
563 ]);
564 vec![
565 DiscoveryStrategy::IOKit,
566 DiscoveryStrategy::SystemCalls,
567 DiscoveryStrategy::NetworkScan,
568 ]
569 }
570 PlatformType::Windows => {
571 supported_hardware.extend(vec![
572 HardwareCategory::GPU,
573 HardwareCategory::Network,
574 HardwareCategory::USB,
575 HardwareCategory::PCI,
576 HardwareCategory::Input,
577 HardwareCategory::Display,
578 HardwareCategory::Bluetooth,
579 ]);
580 vec![
581 DiscoveryStrategy::WMI,
582 DiscoveryStrategy::SystemCalls,
583 DiscoveryStrategy::NetworkScan,
584 ]
585 }
586 PlatformType::WebAssembly => {
587 limitations.push("Limited hardware access in WebAssembly environment".to_string());
588 vec![
589 DiscoveryStrategy::VirtualFS,
590 DiscoveryStrategy::MockFallback,
591 ]
592 }
593 PlatformType::Unknown => {
594 limitations.push("Unknown platform - using fallback strategies".to_string());
595 vec![
596 DiscoveryStrategy::SystemCalls,
597 DiscoveryStrategy::MockFallback,
598 ]
599 }
600 };
601
602 Self {
603 is_fully_supported: platform.platform_type != PlatformType::Unknown,
604 platform_type: platform.platform_type.clone(),
605 supported_hardware,
606 recommended_strategies,
607 limitations,
608 }
609 }
610
611 pub fn supports_category(&self, category: &HardwareCategory) -> bool {
613 self.supported_hardware.contains(category)
614 }
615}
616
617use std::collections::HashMap;