use std::process::Command;
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub enum PlatformType {
Linux,
MacOS,
Windows,
WebAssembly,
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub enum Architecture {
X86_64,
AArch64,
X86,
Arm,
Wasm,
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct PlatformInfo {
pub platform_type: PlatformType,
pub os_name: String,
pub os_version: String,
pub architecture: Architecture,
pub cpu_cores: usize,
pub total_memory: u64,
pub in_container: bool,
pub in_vm: bool,
pub available_strategies: Vec<DiscoveryStrategy>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub enum DiscoveryStrategy {
ProcFs,
SysFs,
DBus,
UDev,
IOKit,
WMI,
Registry,
SystemCalls,
NetworkScan,
VirtualFS,
MockFallback,
}
impl PlatformInfo {
pub fn new() -> Self {
let platform_info = detect_platform();
platform_info
}
pub fn has_strategy(&self, strategy: &DiscoveryStrategy) -> bool {
self.available_strategies.contains(strategy)
}
pub fn best_hardware_strategy(&self) -> DiscoveryStrategy {
match self.platform_type {
PlatformType::Linux => {
if self.has_strategy(&DiscoveryStrategy::SysFs) {
return DiscoveryStrategy::SysFs;
}
if self.has_strategy(&DiscoveryStrategy::ProcFs) {
return DiscoveryStrategy::ProcFs;
}
if self.has_strategy(&DiscoveryStrategy::UDev) {
return DiscoveryStrategy::UDev;
}
DiscoveryStrategy::MockFallback
}
PlatformType::MacOS => {
if self.has_strategy(&DiscoveryStrategy::IOKit) {
return DiscoveryStrategy::IOKit;
}
DiscoveryStrategy::SystemCalls
}
PlatformType::Windows => {
if self.has_strategy(&DiscoveryStrategy::WMI) {
return DiscoveryStrategy::WMI;
}
DiscoveryStrategy::SystemCalls
}
PlatformType::WebAssembly => DiscoveryStrategy::VirtualFS,
PlatformType::Unknown => DiscoveryStrategy::MockFallback,
}
}
pub fn best_network_strategy(&self) -> DiscoveryStrategy {
match self.platform_type {
PlatformType::Linux => {
if self.has_strategy(&DiscoveryStrategy::NetworkScan) {
return DiscoveryStrategy::NetworkScan;
}
DiscoveryStrategy::SysFs
}
PlatformType::MacOS => DiscoveryStrategy::SystemCalls,
PlatformType::Windows => DiscoveryStrategy::WMI,
PlatformType::WebAssembly => DiscoveryStrategy::VirtualFS,
PlatformType::Unknown => DiscoveryStrategy::MockFallback,
}
}
}
impl Default for PlatformInfo {
fn default() -> Self {
Self::new()
}
}
pub fn get_platform_info() -> PlatformInfo {
detect_platform()
}
fn detect_platform() -> PlatformInfo {
let (os_name, os_version) = detect_os();
let architecture = detect_architecture();
let cpu_cores = detect_cpu_cores();
let total_memory = detect_total_memory();
let in_container = detect_container();
let in_vm = detect_virtual_machine();
let platform_type = detect_platform_type(&os_name);
let available_strategies = detect_available_strategies(&platform_type);
PlatformInfo {
platform_type,
os_name,
os_version,
architecture,
cpu_cores,
total_memory,
in_container,
in_vm,
available_strategies,
}
}
fn detect_os() -> (String, String) {
let os = std::env::consts::OS;
let version = std::env::consts::FAMILY;
match os {
"linux" => {
let version_info = read_os_release();
let name = version_info.get("NAME").cloned().unwrap_or_else(|| "Linux".to_string());
let version_id = version_info.get("VERSION_ID")
.or(version_info.get("VERSION"))
.cloned()
.unwrap_or_else(|| "unknown".to_string());
(name, version_id)
}
"macos" => {
let version = detect_macos_version();
("macOS".to_string(), version)
}
"windows" => {
let version = detect_windows_version();
("Windows".to_string(), version)
}
_ => (os.to_string(), version.to_string()),
}
}
fn read_os_release() -> FxHashMap<String, String> {
let mut result = FxHashMap::default();
if let Ok(content) = std::fs::read_to_string("/etc/os-release") {
for line in content.lines() {
if let Some(eq_pos) = line.find('=') {
let key = line[..eq_pos].to_string();
let value = line[eq_pos + 1..].trim_matches('"').to_string();
result.insert(key, value);
}
}
}
result
}
fn detect_macos_version() -> String {
let output = Command::new("sw_vers")
.arg("-productVersion")
.output();
match output {
Ok(output) => {
if output.status.success() {
if let Ok(version) = String::from_utf8(output.stdout) {
return version.trim().to_string();
}
}
}
Err(_) => {}
}
"unknown".to_string()
}
fn detect_windows_version() -> String {
let output = Command::new("cmd")
.args(&["/c", "ver"])
.output();
match output {
Ok(output) => {
if output.status.success() {
if let Ok(version) = String::from_utf8(output.stdout) {
return version.trim().to_string();
}
}
}
Err(_) => {}
}
"unknown".to_string()
}
fn detect_architecture() -> Architecture {
let arch = std::env::consts::ARCH;
match arch {
"x86_64" => Architecture::X86_64,
"aarch64" | "arm64" => Architecture::AArch64,
"x86" | "i686" | "i386" => Architecture::X86,
"arm" | "thumbv7" => Architecture::Arm,
"wasm32" => Architecture::Wasm,
_ => Architecture::Unknown,
}
}
fn detect_cpu_cores() -> usize {
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1)
}
fn detect_total_memory() -> u64 {
if let Ok(content) = std::fs::read_to_string("/proc/meminfo") {
for line in content.lines() {
if line.starts_with("MemTotal:") {
if let Some(kb_str) = line.split_whitespace().nth(1) {
if let Ok(kb) = kb_str.parse::<u64>() {
return kb * 1024;
}
}
}
}
}
4 * 1024 * 1024 * 1024 }
fn detect_container() -> bool {
if std::env::var("DOCKER_CONTAINER").is_ok() {
return true;
}
if std::path::Path::new("/.dockerenv").exists() {
return true;
}
if let Ok(content) = std::fs::read_to_string("/proc/1/cgroup") {
if content.contains("docker") || content.contains("containerd") {
return true;
}
}
if std::env::var("KUBERNETES_SERVICE_HOST").is_ok() {
return true;
}
false
}
fn detect_virtual_machine() -> bool {
if let Ok(content) = std::fs::read_to_string("/sys/hypervisor/type") {
if content.trim() == "xen" || content.contains("vmware") || content.contains("kvm") {
return true;
}
}
if std::path::Path::new("/sys/class/dmi/id/sys_vendor").exists() {
if let Ok(content) = std::fs::read_to_string("/sys/class/dmi/id/sys_vendor") {
let vendor = content.to_lowercase();
if vendor.contains("vmware") || vendor.contains("virtualbox") ||
vendor.contains("qemu") || vendor.contains("hyper-v") {
return true;
}
}
}
false
}
fn detect_platform_type(os_name: &str) -> PlatformType {
let os_lower = os_name.to_lowercase();
if os_lower.contains("linux") || os_lower.contains("ubuntu") ||
os_lower.contains("debian") || os_lower.contains("centos") ||
os_lower.contains("fedora") || os_lower.contains("rhel") ||
os_lower.contains("alpine") {
return PlatformType::Linux;
}
if os_lower.contains("mac") || os_lower.contains("darwin") {
return PlatformType::MacOS;
}
if os_lower.contains("windows") {
return PlatformType::Windows;
}
if std::env::consts::FAMILY == "wasm" {
return PlatformType::WebAssembly;
}
PlatformType::Unknown
}
fn detect_available_strategies(platform_type: &PlatformType) -> Vec<DiscoveryStrategy> {
let mut strategies = Vec::with_capacity(4);
match platform_type {
PlatformType::Linux => {
if std::path::Path::new("/proc").exists() {
strategies.push(DiscoveryStrategy::ProcFs);
}
if std::path::Path::new("/sys").exists() {
strategies.push(DiscoveryStrategy::SysFs);
}
if Command::new("dbus-send").output().is_ok() {
strategies.push(DiscoveryStrategy::DBus);
}
if std::path::Path::new("/dev").exists() {
strategies.push(DiscoveryStrategy::UDev);
}
strategies.push(DiscoveryStrategy::NetworkScan);
strategies.push(DiscoveryStrategy::MockFallback);
}
PlatformType::MacOS => {
strategies.push(DiscoveryStrategy::IOKit);
strategies.push(DiscoveryStrategy::SystemCalls);
strategies.push(DiscoveryStrategy::NetworkScan);
strategies.push(DiscoveryStrategy::MockFallback);
}
PlatformType::Windows => {
if Command::new("wmic").output().is_ok() {
strategies.push(DiscoveryStrategy::WMI);
}
strategies.push(DiscoveryStrategy::Registry);
strategies.push(DiscoveryStrategy::SystemCalls);
strategies.push(DiscoveryStrategy::NetworkScan);
strategies.push(DiscoveryStrategy::MockFallback);
}
PlatformType::WebAssembly => {
strategies.push(DiscoveryStrategy::VirtualFS);
strategies.push(DiscoveryStrategy::MockFallback);
}
PlatformType::Unknown => {
strategies.push(DiscoveryStrategy::SystemCalls);
strategies.push(DiscoveryStrategy::MockFallback);
}
}
strategies
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct PlatformCompatibility {
pub is_fully_supported: bool,
pub platform_type: PlatformType,
pub supported_hardware: Vec<HardwareCategory>,
pub recommended_strategies: Vec<DiscoveryStrategy>,
pub limitations: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub enum HardwareCategory {
CPU,
GPU,
Memory,
Storage,
Network,
USB,
PCI,
Input,
Display,
Bluetooth,
Other,
}
impl PlatformCompatibility {
pub fn current() -> Self {
let platform = get_platform_info();
Self::from_platform(&platform)
}
pub fn from_platform(platform: &PlatformInfo) -> Self {
let mut supported_hardware = Vec::with_capacity(4);
let mut limitations = Vec::with_capacity(4);
supported_hardware.extend(vec![
HardwareCategory::CPU,
HardwareCategory::Memory,
HardwareCategory::Storage,
]);
let recommended_strategies = match platform.platform_type {
PlatformType::Linux => {
supported_hardware.extend(vec![
HardwareCategory::GPU,
HardwareCategory::Network,
HardwareCategory::USB,
HardwareCategory::PCI,
HardwareCategory::Input,
HardwareCategory::Display,
HardwareCategory::Bluetooth,
]);
vec![
DiscoveryStrategy::SysFs,
DiscoveryStrategy::ProcFs,
DiscoveryStrategy::UDev,
DiscoveryStrategy::NetworkScan,
]
}
PlatformType::MacOS => {
supported_hardware.extend(vec![
HardwareCategory::GPU,
HardwareCategory::Network,
HardwareCategory::USB,
HardwareCategory::PCI,
HardwareCategory::Input,
HardwareCategory::Display,
]);
vec![
DiscoveryStrategy::IOKit,
DiscoveryStrategy::SystemCalls,
DiscoveryStrategy::NetworkScan,
]
}
PlatformType::Windows => {
supported_hardware.extend(vec![
HardwareCategory::GPU,
HardwareCategory::Network,
HardwareCategory::USB,
HardwareCategory::PCI,
HardwareCategory::Input,
HardwareCategory::Display,
HardwareCategory::Bluetooth,
]);
vec![
DiscoveryStrategy::WMI,
DiscoveryStrategy::SystemCalls,
DiscoveryStrategy::NetworkScan,
]
}
PlatformType::WebAssembly => {
limitations.push("Limited hardware access in WebAssembly environment".to_string());
vec![
DiscoveryStrategy::VirtualFS,
DiscoveryStrategy::MockFallback,
]
}
PlatformType::Unknown => {
limitations.push("Unknown platform - using fallback strategies".to_string());
vec![
DiscoveryStrategy::SystemCalls,
DiscoveryStrategy::MockFallback,
]
}
};
Self {
is_fully_supported: platform.platform_type != PlatformType::Unknown,
platform_type: platform.platform_type.clone(),
supported_hardware,
recommended_strategies,
limitations,
}
}
pub fn supports_category(&self, category: &HardwareCategory) -> bool {
self.supported_hardware.contains(category)
}
}
use std::collections::HashMap as FxHashMap;