mirror of
https://github.com/RGBCube/superfreq
synced 2025-07-27 17:07:44 +00:00
Merge pull request #10 from NotAShelf/push-lxumomvptrmq
Push lxumomvptrmq
This commit is contained in:
commit
ea84a2997b
6 changed files with 184 additions and 191 deletions
76
src/cpu.rs
76
src/cpu.rs
|
@ -1,46 +1,8 @@
|
|||
use crate::core::{GovernorOverrideMode, TurboSetting};
|
||||
use crate::util::error::ControlError;
|
||||
use core::str;
|
||||
use std::{fs, io, path::Path, string::ToString};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ControlError {
|
||||
Io(io::Error),
|
||||
WriteError(String),
|
||||
InvalidValueError(String),
|
||||
NotSupported(String),
|
||||
PermissionDenied(String),
|
||||
InvalidProfile(String),
|
||||
}
|
||||
|
||||
impl From<io::Error> for ControlError {
|
||||
fn from(err: io::Error) -> Self {
|
||||
match err.kind() {
|
||||
io::ErrorKind::PermissionDenied => Self::PermissionDenied(err.to_string()),
|
||||
_ => Self::Io(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ControlError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Io(e) => write!(f, "I/O error: {e}"),
|
||||
Self::WriteError(s) => write!(f, "Failed to write to sysfs path: {s}"),
|
||||
Self::InvalidValueError(s) => write!(f, "Invalid value for setting: {s}"),
|
||||
Self::NotSupported(s) => write!(f, "Control action not supported: {s}"),
|
||||
Self::PermissionDenied(s) => {
|
||||
write!(f, "Permission denied: {s}. Try running with sudo.")
|
||||
}
|
||||
Self::InvalidProfile(s) => {
|
||||
write!(
|
||||
f,
|
||||
"Invalid platform control profile {s} supplied, please provide a valid one."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ControlError {}
|
||||
|
||||
pub type Result<T, E = ControlError> = std::result::Result<T, E>;
|
||||
|
@ -58,16 +20,13 @@ fn write_sysfs_value(path: impl AsRef<Path>, value: &str) -> Result<()> {
|
|||
})
|
||||
}
|
||||
|
||||
fn for_each_cpu_core<F>(mut action: F) -> Result<()>
|
||||
where
|
||||
F: FnMut(u32) -> Result<()>,
|
||||
{
|
||||
pub fn get_logical_core_count() -> Result<u32> {
|
||||
// Using num_cpus::get() for a reliable count of logical cores accessible.
|
||||
// The monitor module's get_logical_core_count might be more specific to cpufreq-capable cores,
|
||||
// but for applying settings, we might want to iterate over all reported by OS.
|
||||
// However, settings usually apply to cores with cpufreq.
|
||||
// Let's use a similar discovery to monitor's get_logical_core_count
|
||||
let mut cores_to_act_on = Vec::new();
|
||||
let mut num_cores: u32 = 0;
|
||||
let path = Path::new("/sys/devices/system/cpu");
|
||||
if !path.exists() {
|
||||
return Err(ControlError::NotSupported(format!(
|
||||
|
@ -97,20 +56,25 @@ where
|
|||
continue;
|
||||
}
|
||||
|
||||
if let Ok(core_id) = name[3..].parse::<u32>() {
|
||||
cores_to_act_on.push(core_id);
|
||||
if name[3..].parse::<u32>().is_ok() {
|
||||
num_cores += 1;
|
||||
}
|
||||
}
|
||||
if cores_to_act_on.is_empty() {
|
||||
if num_cores == 0 {
|
||||
// Fallback if sysfs iteration above fails to find any cpufreq cores
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let num_cores = num_cpus::get() as u32;
|
||||
for core_id in 0..num_cores {
|
||||
cores_to_act_on.push(core_id);
|
||||
}
|
||||
num_cores = num_cpus::get() as u32;
|
||||
}
|
||||
|
||||
for core_id in cores_to_act_on {
|
||||
Ok(num_cores)
|
||||
}
|
||||
|
||||
fn for_each_cpu_core<F>(mut action: F) -> Result<()>
|
||||
where
|
||||
F: FnMut(u32) -> Result<()>,
|
||||
{
|
||||
let num_cores: u32 = get_logical_core_count()?;
|
||||
|
||||
for core_id in 0u32..num_cores {
|
||||
action(core_id)?;
|
||||
}
|
||||
Ok(())
|
||||
|
@ -264,13 +228,9 @@ pub fn get_platform_profiles() -> Result<Vec<String>> {
|
|||
)));
|
||||
}
|
||||
|
||||
let buf = fs::read(path)
|
||||
let content = fs::read_to_string(path)
|
||||
.map_err(|_| ControlError::PermissionDenied(format!("Cannot read contents of {path}.")))?;
|
||||
|
||||
let content = str::from_utf8(&buf).map_err(|_| {
|
||||
ControlError::NotSupported(format!("No platform profile choices found at {path}."))
|
||||
})?;
|
||||
|
||||
Ok(content
|
||||
.split_whitespace()
|
||||
.map(ToString::to_string)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::config::{AppConfig, ProfileConfig};
|
||||
use crate::core::{OperationalMode, SystemReport, TurboSetting};
|
||||
use crate::cpu::{self, ControlError};
|
||||
use crate::cpu::{self};
|
||||
use crate::util::error::ControlError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EngineError {
|
||||
|
@ -67,7 +68,7 @@ pub fn determine_and_apply_settings(
|
|||
// Otherwise, check the ac_connected status from the (first) battery.
|
||||
// XXX: This relies on the setting ac_connected in BatteryInfo being set correctly.
|
||||
let on_ac_power = report.batteries.is_empty()
|
||||
|| report.batteries.first().is_some_and(|b| b.ac_connected);
|
||||
|| report.batteries.first().map_or(false, |b| b.ac_connected);
|
||||
|
||||
if on_ac_power {
|
||||
println!("Engine: On AC power, selecting Charger profile.");
|
||||
|
|
|
@ -5,9 +5,11 @@ mod cpu;
|
|||
mod daemon;
|
||||
mod engine;
|
||||
mod monitor;
|
||||
mod util;
|
||||
|
||||
use crate::config::AppConfig;
|
||||
use crate::core::{GovernorOverrideMode, TurboSetting};
|
||||
use crate::util::error::ControlError;
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
|
@ -216,8 +218,8 @@ fn main() {
|
|||
// For example, check if e.downcast_ref::<cpu::ControlError>() matches PermissionDenied
|
||||
// and print a more specific message like "Try running with sudo."
|
||||
// We'll revisit this in the future once CPU logic is more stable.
|
||||
if let Some(control_error) = e.downcast_ref::<cpu::ControlError>() {
|
||||
if matches!(control_error, cpu::ControlError::PermissionDenied(_)) {
|
||||
if let Some(control_error) = e.downcast_ref::<ControlError>() {
|
||||
if matches!(control_error, ControlError::PermissionDenied(_)) {
|
||||
eprintln!(
|
||||
"Hint: This operation may require administrator privileges (e.g., run with sudo)."
|
||||
);
|
||||
|
|
218
src/monitor.rs
218
src/monitor.rs
|
@ -1,5 +1,8 @@
|
|||
use crate::config::AppConfig;
|
||||
use crate::core::{BatteryInfo, CpuCoreInfo, CpuGlobalInfo, SystemInfo, SystemLoad, SystemReport};
|
||||
use crate::cpu::{get_logical_core_count, get_platform_profiles};
|
||||
use crate::util::error::ControlError;
|
||||
use crate::util::error::SysMonitorError;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs, io,
|
||||
|
@ -10,35 +13,6 @@ use std::{
|
|||
time::SystemTime,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SysMonitorError {
|
||||
Io(io::Error),
|
||||
ReadError(String),
|
||||
ParseError(String),
|
||||
ProcStatParseError(String),
|
||||
NotAvailable(String),
|
||||
}
|
||||
|
||||
impl From<io::Error> for SysMonitorError {
|
||||
fn from(err: io::Error) -> Self {
|
||||
Self::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SysMonitorError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Io(e) => write!(f, "I/O error: {e}"),
|
||||
Self::ReadError(s) => write!(f, "Failed to read sysfs path: {s}"),
|
||||
Self::ParseError(s) => write!(f, "Failed to parse value: {s}"),
|
||||
Self::ProcStatParseError(s) => {
|
||||
write!(f, "Failed to parse /proc/stat: {s}")
|
||||
}
|
||||
Self::NotAvailable(s) => write!(f, "Information not available: {s}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for SysMonitorError {}
|
||||
|
||||
pub type Result<T, E = SysMonitorError> = std::result::Result<T, E>;
|
||||
|
@ -64,83 +38,15 @@ fn read_sysfs_value<T: FromStr>(path: impl AsRef<Path>) -> Result<T> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn get_system_info() -> Result<SystemInfo> {
|
||||
let mut cpu_model = "Unknown".to_string();
|
||||
if let Ok(cpuinfo) = fs::read_to_string("/proc/cpuinfo") {
|
||||
for line in cpuinfo.lines() {
|
||||
if line.starts_with("model name") {
|
||||
if let Some(val) = line.split(':').nth(1) {
|
||||
cpu_model = val.trim().to_string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_system_info() -> SystemInfo {
|
||||
let cpu_model = get_cpu_model().unwrap_or_else(|_| "Unknown".to_string());
|
||||
let linux_distribution = get_linux_distribution().unwrap_or_else(|_| "Unknown".to_string());
|
||||
let architecture = std::env::consts::ARCH.to_string();
|
||||
|
||||
let mut linux_distribution = "Unknown".to_string();
|
||||
if let Ok(os_release) = fs::read_to_string("/etc/os-release") {
|
||||
for line in os_release.lines() {
|
||||
if line.starts_with("PRETTY_NAME=") {
|
||||
if let Some(val) = line.split('=').nth(1) {
|
||||
linux_distribution = val.trim_matches('"').to_string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Ok(lsb_release) = fs::read_to_string("/etc/lsb-release") {
|
||||
// fallback for some systems
|
||||
for line in lsb_release.lines() {
|
||||
if line.starts_with("DISTRIB_DESCRIPTION=") {
|
||||
if let Some(val) = line.split('=').nth(1) {
|
||||
linux_distribution = val.trim_matches('"').to_string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(SystemInfo {
|
||||
SystemInfo {
|
||||
cpu_model,
|
||||
architecture,
|
||||
linux_distribution,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_logical_core_count() -> Result<u32> {
|
||||
let mut count = 0;
|
||||
let path = Path::new("/sys/devices/system/cpu");
|
||||
if path.exists() {
|
||||
for entry in fs::read_dir(path)? {
|
||||
let entry = entry?;
|
||||
let name = entry.file_name();
|
||||
if let Some(name_str) = name.to_str() {
|
||||
if name_str.starts_with("cpu")
|
||||
&& name_str.len() > 3
|
||||
&& name_str[3..].chars().all(char::is_numeric)
|
||||
{
|
||||
// Check if it's a directory representing a core that can have cpufreq
|
||||
if entry.path().join("cpufreq").exists() {
|
||||
count += 1;
|
||||
} else if Path::new(&format!("/sys/devices/system/cpu/{name_str}/online"))
|
||||
.exists()
|
||||
{
|
||||
// Fallback for cores that might not have cpufreq but are online (e.g. E-cores on some setups before driver loads)
|
||||
// This is a simplification; true cpufreq capability is key.
|
||||
// If cpufreq dir doesn't exist, it might not be controllable by this tool.
|
||||
// For counting purposes, we count it if it's an online CPU.
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if count == 0 {
|
||||
// Fallback to num_cpus crate if sysfs parsing fails or yields 0
|
||||
Ok(num_cpus::get() as u32)
|
||||
} else {
|
||||
Ok(count)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -474,7 +380,9 @@ pub fn get_all_cpu_core_info() -> Result<Vec<CpuCoreInfo>> {
|
|||
thread::sleep(Duration::from_millis(250)); // Interval for CPU usage calculation
|
||||
let final_cpu_times = read_all_cpu_times()?;
|
||||
|
||||
let num_cores = get_logical_core_count()?; // Or derive from keys in cpu_times
|
||||
let num_cores = get_logical_core_count()
|
||||
.map_err(|_| SysMonitorError::ReadError("Could not get the number of cores".to_string()))?;
|
||||
|
||||
let mut core_infos = Vec::with_capacity(num_cores as usize);
|
||||
|
||||
for core_id in 0..num_cores {
|
||||
|
@ -497,44 +405,38 @@ pub fn get_all_cpu_core_info() -> Result<Vec<CpuCoreInfo>> {
|
|||
Ok(core_infos)
|
||||
}
|
||||
|
||||
pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> Result<CpuGlobalInfo> {
|
||||
pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> CpuGlobalInfo {
|
||||
// FIXME: Assume global settings can be read from cpu0 or are consistent.
|
||||
// This might not work properly for heterogeneous systems (e.g. big.LITTLE)
|
||||
let cpufreq_base = Path::new("/sys/devices/system/cpu/cpu0/cpufreq/");
|
||||
|
||||
let current_governor = if cpufreq_base.join("scaling_governor").exists() {
|
||||
read_sysfs_file_trimmed(cpufreq_base.join("scaling_governor")).ok()
|
||||
let cpufreq_base_path = Path::new("/sys/devices/system/cpu/cpu0/cpufreq/");
|
||||
let turbo_status_path = Path::new("/sys/devices/system/cpu/intel_pstate/no_turbo");
|
||||
let boost_path = Path::new("/sys/devices/system/cpu/cpufreq/boost");
|
||||
let current_governor = if cpufreq_base_path.join("scaling_governor").exists() {
|
||||
read_sysfs_file_trimmed(cpufreq_base_path.join("scaling_governor")).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let available_governors = if cpufreq_base.join("scaling_available_governors").exists() {
|
||||
read_sysfs_file_trimmed(cpufreq_base.join("scaling_available_governors")).map_or_else(|_| vec![], |s| s.split_whitespace().map(String::from).collect())
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let available_governors = get_platform_profiles().unwrap_or_else(|_| vec![]);
|
||||
|
||||
let turbo_status = if Path::new("/sys/devices/system/cpu/intel_pstate/no_turbo").exists() {
|
||||
let turbo_status = if turbo_status_path.exists() {
|
||||
// 0 means turbo enabled, 1 means disabled for intel_pstate
|
||||
read_sysfs_value::<u8>("/sys/devices/system/cpu/intel_pstate/no_turbo")
|
||||
read_sysfs_value::<u8>(turbo_status_path)
|
||||
.map(|val| val == 0)
|
||||
.ok()
|
||||
} else if Path::new("/sys/devices/system/cpu/cpufreq/boost").exists() {
|
||||
} else if boost_path.exists() {
|
||||
// 1 means turbo enabled, 0 means disabled for generic cpufreq boost
|
||||
read_sysfs_value::<u8>("/sys/devices/system/cpu/cpufreq/boost")
|
||||
.map(|val| val == 1)
|
||||
.ok()
|
||||
read_sysfs_value::<u8>(boost_path).map(|val| val == 1).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// EPP (Energy Performance Preference)
|
||||
let energy_perf_pref =
|
||||
read_sysfs_file_trimmed(cpufreq_base.join("energy_performance_preference")).ok();
|
||||
read_sysfs_file_trimmed(cpufreq_base_path.join("energy_performance_preference")).ok();
|
||||
|
||||
// EPB (Energy Performance Bias)
|
||||
let energy_perf_bias =
|
||||
read_sysfs_file_trimmed(cpufreq_base.join("energy_performance_bias")).ok();
|
||||
read_sysfs_file_trimmed(cpufreq_base_path.join("energy_performance_bias")).ok();
|
||||
|
||||
let platform_profile = read_sysfs_file_trimmed("/sys/firmware/acpi/platform_profile").ok();
|
||||
let _platform_profile_choices =
|
||||
|
@ -562,7 +464,7 @@ pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> Result<CpuGlobalInfo> {
|
|||
}
|
||||
};
|
||||
|
||||
Ok(CpuGlobalInfo {
|
||||
CpuGlobalInfo {
|
||||
current_governor,
|
||||
available_governors,
|
||||
turbo_status,
|
||||
|
@ -570,7 +472,7 @@ pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> Result<CpuGlobalInfo> {
|
|||
epb: energy_perf_bias,
|
||||
platform_profile,
|
||||
average_temperature_celsius,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_battery_info(config: &AppConfig) -> Result<Vec<BatteryInfo>> {
|
||||
|
@ -581,10 +483,7 @@ pub fn get_battery_info(config: &AppConfig) -> Result<Vec<BatteryInfo>> {
|
|||
return Ok(batteries); // no power supply directory
|
||||
}
|
||||
|
||||
let ignored_supplies = config
|
||||
.ignored_power_supplies
|
||||
.clone()
|
||||
.unwrap_or_default();
|
||||
let ignored_supplies = config.ignored_power_supplies.clone().unwrap_or_default();
|
||||
|
||||
// Determine overall AC connection status
|
||||
let mut overall_ac_connected = false;
|
||||
|
@ -701,9 +600,9 @@ pub fn get_system_load() -> Result<SystemLoad> {
|
|||
}
|
||||
|
||||
pub fn collect_system_report(config: &AppConfig) -> Result<SystemReport> {
|
||||
let system_info = get_system_info()?;
|
||||
let system_info = get_system_info();
|
||||
let cpu_cores = get_all_cpu_core_info()?;
|
||||
let cpu_global = get_cpu_global_info(&cpu_cores)?;
|
||||
let cpu_global = get_cpu_global_info(&cpu_cores);
|
||||
let batteries = get_battery_info(config)?;
|
||||
let system_load = get_system_load()?;
|
||||
|
||||
|
@ -716,3 +615,64 @@ pub fn collect_system_report(config: &AppConfig) -> Result<SystemReport> {
|
|||
timestamp: SystemTime::now(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_cpu_model() -> Result<String> {
|
||||
let path = Path::new("/proc/cpuinfo");
|
||||
let content = fs::read_to_string(path).map_err(|_| {
|
||||
SysMonitorError::ReadError(format!("Cannot read contents of {}.", path.display()))
|
||||
})?;
|
||||
|
||||
for line in content.lines() {
|
||||
if line.starts_with("model name") {
|
||||
if let Some(val) = line.split(':').nth(1) {
|
||||
let cpu_model = val.trim().to_string();
|
||||
return Ok(cpu_model);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(SysMonitorError::ParseError(
|
||||
"Could not find CPU model name in /proc/cpuinfo.".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_linux_distribution() -> Result<String> {
|
||||
let os_release_path = Path::new("/etc/os-release");
|
||||
let content = fs::read_to_string(os_release_path).map_err(|_| {
|
||||
SysMonitorError::ReadError(format!(
|
||||
"Cannot read contents of {}.",
|
||||
os_release_path.display()
|
||||
))
|
||||
})?;
|
||||
|
||||
for line in content.lines() {
|
||||
if line.starts_with("PRETTY_NAME=") {
|
||||
if let Some(val) = line.split('=').nth(1) {
|
||||
let linux_distribution = val.trim_matches('"').to_string();
|
||||
return Ok(linux_distribution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let lsb_release_path = Path::new("/etc/lsb-release");
|
||||
let content = fs::read_to_string(lsb_release_path).map_err(|_| {
|
||||
SysMonitorError::ReadError(format!(
|
||||
"Cannot read contents of {}.",
|
||||
lsb_release_path.display()
|
||||
))
|
||||
})?;
|
||||
|
||||
for line in content.lines() {
|
||||
if line.starts_with("DISTRIB_DESCRIPTION=") {
|
||||
if let Some(val) = line.split('=').nth(1) {
|
||||
let linux_distribution = val.trim_matches('"').to_string();
|
||||
return Ok(linux_distribution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(SysMonitorError::ParseError(format!(
|
||||
"Could not find distribution name in {} or {}.",
|
||||
os_release_path.display(),
|
||||
lsb_release_path.display()
|
||||
)))
|
||||
}
|
||||
|
|
69
src/util/error.rs
Normal file
69
src/util/error.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
use std::io;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ControlError {
|
||||
Io(io::Error),
|
||||
WriteError(String),
|
||||
InvalidValueError(String),
|
||||
NotSupported(String),
|
||||
PermissionDenied(String),
|
||||
InvalidProfile(String),
|
||||
}
|
||||
|
||||
impl From<io::Error> for ControlError {
|
||||
fn from(err: io::Error) -> Self {
|
||||
match err.kind() {
|
||||
io::ErrorKind::PermissionDenied => Self::PermissionDenied(err.to_string()),
|
||||
_ => Self::Io(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ControlError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Io(e) => write!(f, "I/O error: {e}"),
|
||||
Self::WriteError(s) => write!(f, "Failed to write to sysfs path: {s}"),
|
||||
Self::InvalidValueError(s) => write!(f, "Invalid value for setting: {s}"),
|
||||
Self::NotSupported(s) => write!(f, "Control action not supported: {s}"),
|
||||
Self::PermissionDenied(s) => {
|
||||
write!(f, "Permission denied: {s}. Try running with sudo.")
|
||||
}
|
||||
Self::InvalidProfile(s) => {
|
||||
write!(
|
||||
f,
|
||||
"Invalid platform control profile {s} supplied, please provide a valid one."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SysMonitorError {
|
||||
Io(io::Error),
|
||||
ReadError(String),
|
||||
ParseError(String),
|
||||
ProcStatParseError(String),
|
||||
NotAvailable(String),
|
||||
}
|
||||
|
||||
impl From<io::Error> for SysMonitorError {
|
||||
fn from(err: io::Error) -> Self {
|
||||
Self::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SysMonitorError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Io(e) => write!(f, "I/O error: {e}"),
|
||||
Self::ReadError(s) => write!(f, "Failed to read sysfs path: {s}"),
|
||||
Self::ParseError(s) => write!(f, "Failed to parse value: {s}"),
|
||||
Self::ProcStatParseError(s) => {
|
||||
write!(f, "Failed to parse /proc/stat: {s}")
|
||||
}
|
||||
Self::NotAvailable(s) => write!(f, "Information not available: {s}"),
|
||||
}
|
||||
}
|
||||
}
|
1
src/util/mod.rs
Normal file
1
src/util/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod error;
|
Loading…
Add table
Add a link
Reference in a new issue