mod mock;
mod real;
pub use mock::MockHardware;
pub use real::detect_real_hardware;
use serde::{Deserialize, Serialize};
use crate::fingerprint::Fingerprint;
use crate::knowledge::{KnowledgePack, Microarch, ValidationReport};
use crate::topology::TopologyGraph;
use crate::{Result, SiliceraError};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SupportStatus {
Supported {
microarch: Microarch,
},
UnsupportedMicroarch {
reason: String,
family: Option<u32>,
model: Option<u32>,
},
Unsupported {
reason: String,
},
}
impl SupportStatus {
pub fn is_supported(&self) -> bool {
matches!(self, SupportStatus::Supported { .. })
}
pub fn message(&self) -> String {
match self {
SupportStatus::Supported { microarch } => {
format!("supported AMD microarchitecture: {microarch}")
}
SupportStatus::UnsupportedMicroarch { reason, .. } => {
format!("unsupported AMD microarchitecture: {reason}")
}
SupportStatus::Unsupported { reason } => {
format!("unsupported CPU: {reason}")
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HardwareInfo {
pub brand: String,
pub vendor: String,
pub family: u32,
pub model: u32,
pub stepping: u32,
pub support: SupportStatus,
pub topology: TopologyGraph,
pub fingerprint: Option<Fingerprint>,
pub validation: Option<ValidationReportDto>,
pub is_mock: bool,
pub environment: EnvironmentSnapshot,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationReportDto {
pub warnings: Vec<String>,
pub errors: Vec<String>,
}
impl From<ValidationReport> for ValidationReportDto {
fn from(r: ValidationReport) -> Self {
Self {
warnings: r.warnings,
errors: r.errors,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnvironmentSnapshot {
pub captured_at: String,
pub os: String,
pub os_version: String,
pub arch: String,
pub logical_cpus: usize,
pub power_hint: String,
}
impl EnvironmentSnapshot {
pub fn capture() -> Self {
Self {
captured_at: chrono::Utc::now().to_rfc3339(),
os: std::env::consts::OS.to_string(),
os_version: os_version_string(),
arch: std::env::consts::ARCH.to_string(),
logical_cpus: std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1),
power_hint: String::new(),
}
}
}
fn os_version_string() -> String {
#[cfg(target_os = "windows")]
{
std::env::var("OS").unwrap_or_else(|_| "Windows".into())
}
#[cfg(not(target_os = "windows"))]
{
"unknown".into()
}
}
pub trait HardwareBackend: Send + Sync {
fn discover(&self, knowledge: &KnowledgePack) -> Result<HardwareInfo>;
}
pub fn detect_hardware() -> Result<HardwareInfo> {
let knowledge = KnowledgePack::builtin();
detect_real_hardware(&knowledge)
}
pub fn detect_hardware_with(knowledge: &KnowledgePack) -> Result<HardwareInfo> {
detect_real_hardware(knowledge)
}
pub fn require_supported(info: &HardwareInfo) -> Result<Microarch> {
match &info.support {
SupportStatus::Supported { microarch } => Ok(*microarch),
SupportStatus::UnsupportedMicroarch { reason, .. } => {
Err(SiliceraError::UnsupportedMicroarch(reason.clone()))
}
SupportStatus::Unsupported { reason } => {
Err(SiliceraError::UnsupportedCpu(reason.clone()))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mock_zen5_supported() {
let kp = KnowledgePack::builtin();
let hw = MockHardware::zen5_dual_ccd();
let info = hw.discover(&kp).unwrap();
assert!(info.support.is_supported());
assert!(info.fingerprint.is_some());
assert!(info.is_mock);
}
#[test]
fn mock_unsupported_graceful() {
let kp = KnowledgePack::builtin();
let hw = MockHardware::unsupported_intel();
let info = hw.discover(&kp).unwrap();
assert!(!info.support.is_supported());
assert!(info.fingerprint.is_none());
}
}