dmsc/device/discovery/
platform.rs

1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of DMSC.
4//! The DMSC project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//!     http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18//! # Platform Detection Module
19//!
20//! This module provides cross-platform hardware detection capabilities.
21//! It automatically detects the operating system and platform characteristics
22//! to enable appropriate discovery strategies for any hardware type.
23//!
24//! ## Supported Platforms
25//!
26//! - **Linux**: Full support including sysfs, procfs, D-Bus, udev
27//! - **macOS**: Full support using IOKit and system calls
28//! - **Windows**: Full support using WMI and system APIs
29//! - **WebAssembly**: Limited support with virtual devices
30//!
31//! ## Usage
32//!
33//! ```rust,ignore
34//! use dmsc::device::discovery::platform::{PlatformInfo, PlatformType, get_platform_info};
35//!
36//! let platform = get_platform_info();
37//! println!("Platform: {:?}", platform.platform_type);
38//! println!("OS: {}", platform.os_name);
39//! println!("Architecture: {}", platform.arch);
40//! ```
41
42use std::process::Command;
43use serde::{Serialize, Deserialize};
44
45/// Supported platform types
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
48pub enum PlatformType {
49    /// Linux-based operating systems
50    Linux,
51    /// macOS (Darwin)
52    MacOS,
53    /// Microsoft Windows
54    Windows,
55    /// WebAssembly environment
56    WebAssembly,
57    /// Unknown or unsupported platform
58    Unknown,
59}
60
61/// CPU architecture types
62#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
63#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
64pub enum Architecture {
65    /// x86_64 (AMD64)
66    X86_64,
67    /// AArch64 (ARM64)
68    AArch64,
69    /// x86 (i386/i686)
70    X86,
71    /// ARM (32-bit)
72    Arm,
73    /// WebAssembly
74    Wasm,
75    /// Unknown architecture
76    Unknown,
77}
78
79/// Platform information container
80#[derive(Debug, Clone, Serialize, Deserialize)]
81#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
82pub struct PlatformInfo {
83    /// Detected platform type
84    pub platform_type: PlatformType,
85    /// Operating system name
86    pub os_name: String,
87    /// Operating system version
88    pub os_version: String,
89    /// CPU architecture
90    pub architecture: Architecture,
91    /// Number of available CPU cores
92    pub cpu_cores: usize,
93    /// Amount of available memory in bytes
94    pub total_memory: u64,
95    /// Whether running in a container
96    pub in_container: bool,
97    /// Whether running in a virtual machine
98    pub in_vm: bool,
99    /// Available discovery strategies for this platform
100    pub available_strategies: Vec<DiscoveryStrategy>,
101}
102
103/// Discovery strategies available on the current platform
104#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
105#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
106pub enum DiscoveryStrategy {
107    /// Read from /proc filesystem (Linux)
108    ProcFs,
109    /// Read from /sys filesystem (Linux)
110    SysFs,
111    /// Use D-Bus for device enumeration (Linux)
112    DBus,
113    /// Use udev for device enumeration (Linux)
114    UDev,
115    /// Use IOKit (macOS)
116    IOKit,
117    /// Use WMI (Windows)
118    WMI,
119    /// Use Windows Registry
120    Registry,
121    /// Use system calls and APIs
122    SystemCalls,
123    /// Use network scanning
124    NetworkScan,
125    /// Use virtual filesystem
126    VirtualFS,
127    /// Fallback to mock devices
128    MockFallback,
129}
130
131impl PlatformInfo {
132    /// Creates a new platform info instance with detected values
133    pub fn new() -> Self {
134        let platform_info = detect_platform();
135        platform_info
136    }
137
138    /// Checks if a specific discovery strategy is available
139    pub fn has_strategy(&self, strategy: &DiscoveryStrategy) -> bool {
140        self.available_strategies.contains(strategy)
141    }
142
143    /// Returns the best available discovery strategy for hardware
144    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    /// Returns the best available discovery strategy for network devices
176    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
198/// Detects the current platform and returns platform information
199pub fn get_platform_info() -> PlatformInfo {
200    detect_platform()
201}
202
203/// Detects the current platform
204fn 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
227/// Detects the operating system name and version
228fn 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
254/// Reads /etc/os-release for Linux distribution info
255fn 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
271/// Detects macOS version
272fn 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
291/// Detects Windows version
292fn 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
311/// Detects the CPU architecture
312fn 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
325/// Detects the number of CPU cores
326fn detect_cpu_cores() -> usize {
327    std::thread::available_parallelism()
328        .map(|n| n.get())
329        .unwrap_or(1)
330}
331
332/// Detects total system memory
333fn 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 // Default to 4GB if detection fails
346}
347
348/// Detects if running inside a container
349fn detect_container() -> bool {
350    // Check for container-specific files and environment variables
351
352    // Docker
353    if std::env::var("DOCKER_CONTAINER").is_ok() {
354        return true;
355    }
356
357    // Check for .dockerenv file
358    if std::path::Path::new("/.dockerenv").exists() {
359        return true;
360    }
361
362    // Check for container cgroup
363    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    // Check for k8s
370    if std::env::var("KUBERNETES_SERVICE_HOST").is_ok() {
371        return true;
372    }
373
374    false
375}
376
377/// Detects if running inside a virtual machine
378fn detect_virtual_machine() -> bool {
379    // Check for hypervisor
380    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    // Check for VMware
387    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
400/// Detects the platform type from OS name
401fn 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
426/// Detects available discovery strategies for the platform
427fn 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/// Platform detection result for hardware compatibility
476#[derive(Debug, Clone, Serialize, Deserialize)]
477#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
478pub struct PlatformCompatibility {
479    /// Whether the platform is fully supported
480    pub is_fully_supported: bool,
481    /// Platform type
482    pub platform_type: PlatformType,
483    /// List of supported hardware categories
484    pub supported_hardware: Vec<HardwareCategory>,
485    /// Recommended discovery strategies
486    pub recommended_strategies: Vec<DiscoveryStrategy>,
487    /// Any limitations
488    pub limitations: Vec<String>,
489}
490
491/// Categories of hardware that can be discovered
492#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
493#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
494pub enum HardwareCategory {
495    /// CPU and processor devices
496    CPU,
497    /// GPU and graphics processors
498    GPU,
499    /// Memory and RAM
500    Memory,
501    /// Storage devices (HDD, SSD, NVMe)
502    Storage,
503    /// Network interfaces and adapters
504    Network,
505    /// USB devices
506    USB,
507    /// PCI devices
508    PCI,
509    /// Input devices (keyboard, mouse)
510    Input,
511    /// Display devices (monitors)
512    Display,
513    /// Bluetooth devices
514    Bluetooth,
515    /// Other or custom devices
516    Other,
517}
518
519impl PlatformCompatibility {
520    /// Creates platform compatibility info for current platform
521    pub fn current() -> Self {
522        let platform = get_platform_info();
523        Self::from_platform(&platform)
524    }
525
526    /// Creates platform compatibility from platform info
527    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    /// Checks if a hardware category is supported
612    pub fn supports_category(&self, category: &HardwareCategory) -> bool {
613        self.supported_hardware.contains(category)
614    }
615}
616
617use std::collections::HashMap;