use crate::Error;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Frequency {
pub base: Option<f64>,
pub current: Option<f64>,
pub max: Option<f64>,
}
impl fmt::Display for Frequency {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let base = self
.base
.map_or_else(|| "Unknown".to_string(), |v| format!("{v:.2} MHz"));
let current = self
.current
.map_or_else(|| "Unknown".to_string(), |v| format!("{v:.2} MHz"));
let max = self
.max
.map_or_else(|| "Unknown".to_string(), |v| format!("{v:.2} MHz"));
write!(f, "Base: {base}, Current: {current}, Max: {max}")
}
}
pub fn detect_frequency() -> Result<Frequency, Error> {
#[cfg(feature = "frequency")]
{
#[cfg(target_os = "linux")]
return Ok(detect_frequency_linux());
#[cfg(target_os = "windows")]
return detect_frequency_windows();
#[cfg(target_os = "macos")]
return detect_frequency_macos();
#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
return Ok(detect_frequency_generic());
}
#[cfg(not(feature = "frequency"))]
{
Ok(Frequency::default())
}
}
#[cfg(all(feature = "frequency", target_os = "linux"))]
fn detect_frequency_linux() -> Frequency {
use std::fs::read_to_string;
let mut frequency = Frequency::default();
if let Ok(content) = read_to_string("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq")
&& let Ok(khz) = content.trim().parse::<f64>()
{
frequency.current = Some(khz / 1000.0);
}
if let Ok(content) = read_to_string("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq")
&& let Ok(khz) = content.trim().parse::<f64>()
{
frequency.max = Some(khz / 1000.0);
}
if let Ok(content) = read_to_string("/sys/devices/system/cpu/cpu0/cpufreq/base_frequency")
&& let Ok(khz) = content.trim().parse::<f64>()
{
frequency.base = Some(khz / 1000.0);
}
if frequency.current.is_none() && frequency.max.is_none() && frequency.base.is_none() {
return detect_frequency_generic();
}
frequency
}
#[cfg(all(feature = "frequency", target_os = "windows"))]
fn detect_frequency_windows() -> Result<Frequency, Error> {
use serde::Deserialize;
use sysinfo::{CpuRefreshKind, System};
use wmi::{COMLibrary, WMIConnection};
#[derive(Deserialize, Debug)]
struct Win32_Processor {
CurrentClockSpeed: Option<u32>,
MaxClockSpeed: Option<u32>,
}
let mut frequency = Frequency::default();
match COMLibrary::new() {
Ok(com_lib) => {
if let Ok(wmi_con) = WMIConnection::new(com_lib) {
if let Ok(processors) = wmi_con.query::<Win32_Processor>() {
if let Some(processor) = processors.first() {
if let Some(current_speed) = processor.CurrentClockSpeed {
frequency.current = Some(current_speed as f64);
}
if let Some(max_speed) = processor.MaxClockSpeed {
frequency.max = Some(max_speed as f64);
if frequency.base.is_none() {
frequency.base = Some(max_speed as f64 * 0.8);
}
}
}
}
}
},
Err(e) => {
eprintln!("Failed to initialize COM library for WMI: {}", e);
},
}
if frequency.current.is_none() {
let mut system = System::new();
system.refresh_cpu_specifics(CpuRefreshKind::everything());
if let Some(cpu) = system.cpus().first() {
frequency.current = Some(cpu.frequency() as f64);
}
}
if frequency.current.is_none() && frequency.max.is_none() && frequency.base.is_none() {
return Ok(detect_frequency_generic());
}
Ok(frequency)
}
#[cfg(all(feature = "frequency", target_os = "macos"))]
#[allow(clippy::unnecessary_wraps)]
fn detect_frequency_macos() -> Result<Frequency, Error> {
Ok(detect_frequency_macos_inner())
}
#[cfg(all(feature = "frequency", target_os = "macos"))]
fn detect_frequency_macos_inner() -> Frequency {
use sysctl::{CtlValue, Sysctl};
let mut frequency = Frequency::default();
#[allow(clippy::cast_precision_loss)]
{
if let Ok(ctl) = sysctl::Ctl::new("hw.cpufrequency")
&& let Ok(CtlValue::S64(freq)) = ctl.value()
{
frequency.current = Some((freq as f64) / 1_000_000.0);
}
if let Ok(ctl) = sysctl::Ctl::new("hw.cpufrequency_max")
&& let Ok(CtlValue::S64(freq)) = ctl.value()
{
frequency.max = Some((freq as f64) / 1_000_000.0);
}
if let Ok(ctl) = sysctl::Ctl::new("hw.cpufrequency_min")
&& let Ok(CtlValue::S64(freq)) = ctl.value()
{
frequency.base = Some((freq as f64) / 1_000_000.0);
}
}
if frequency.current.is_none() && frequency.max.is_none() && frequency.base.is_none() {
return detect_frequency_generic();
}
frequency
}
#[cfg(feature = "frequency")]
fn detect_frequency_generic() -> Frequency {
use sysinfo::{CpuRefreshKind, System};
let mut frequency = Frequency::default();
let mut system = System::new();
system.refresh_cpu_specifics(CpuRefreshKind::everything());
if let Some(cpu) = system.cpus().first() {
#[allow(clippy::cast_precision_loss)]
let freq = cpu.frequency() as f64;
frequency.current = Some(freq);
}
if let Some(current) = frequency.current {
frequency.base = Some(current * 0.9);
frequency.max = Some(current * 1.1);
}
frequency
}