From a14d88cee7e3ca70ebf13defc482dea29deba94c Mon Sep 17 00:00:00 2001 From: RGBCube Date: Mon, 19 May 2025 17:43:21 +0300 Subject: [PATCH] wip unsound broken malfunctioning changes to make it compile --- src/cli/debug.rs | 265 -------------------------------------------- src/cli/mod.rs | 1 - src/config/load.rs | 25 +++-- src/config/types.rs | 112 +++++++------------ src/daemon.rs | 37 +++---- src/engine.rs | 67 ++++------- src/main.rs | 4 +- src/monitor.rs | 24 ++-- src/util/error.rs | 80 ------------- src/util/mod.rs | 2 - src/util/sysfs.rs | 80 ------------- 11 files changed, 106 insertions(+), 591 deletions(-) delete mode 100644 src/cli/debug.rs delete mode 100644 src/cli/mod.rs delete mode 100644 src/util/error.rs delete mode 100644 src/util/mod.rs delete mode 100644 src/util/sysfs.rs diff --git a/src/cli/debug.rs b/src/cli/debug.rs deleted file mode 100644 index 17cec0c..0000000 --- a/src/cli/debug.rs +++ /dev/null @@ -1,265 +0,0 @@ -use crate::config::AppConfig; -use crate::cpu; -use crate::monitor; -use crate::util::error::AppError; -use std::fs; -use std::process::{Command, Stdio}; -use std::time::Duration; - -/// Prints comprehensive debug information about the system -pub fn run_debug(config: &AppConfig) -> Result<(), AppError> { - println!("=== SUPERFREQ DEBUG INFORMATION ==="); - println!("Version: {}", env!("CARGO_PKG_VERSION")); - - // Current date and time - println!("Timestamp: {}", jiff::Timestamp::now()); - - // Kernel information - if let Ok(kernel_info) = get_kernel_info() { - println!("Kernel Version: {kernel_info}"); - } else { - println!("Kernel Version: Unable to determine"); - } - - // System uptime - if let Ok(uptime) = get_system_uptime() { - println!( - "System Uptime: {} hours, {} minutes", - uptime.as_secs() / 3600, - (uptime.as_secs() % 3600) / 60 - ); - } else { - println!("System Uptime: Unable to determine"); - } - - // Get system information - match monitor::collect_system_report(config) { - Ok(report) => { - println!("\n--- SYSTEM INFORMATION ---"); - println!("CPU Model: {}", report.system_info.cpu_model); - println!("Architecture: {}", report.system_info.architecture); - println!( - "Linux Distribution: {}", - report.system_info.linux_distribution - ); - - println!("\n--- CONFIGURATION ---"); - println!("Current Configuration: {config:#?}"); - - // Print important sysfs paths and whether they exist - println!("\n--- SYSFS PATHS ---"); - check_and_print_sysfs_path( - "/sys/devices/system/cpu/intel_pstate/no_turbo", - "Intel P-State Turbo Control", - ); - check_and_print_sysfs_path( - "/sys/devices/system/cpu/cpufreq/boost", - "Generic CPU Boost Control", - ); - check_and_print_sysfs_path( - "/sys/devices/system/cpu/amd_pstate/cpufreq/boost", - "AMD P-State Boost Control", - ); - check_and_print_sysfs_path( - "/sys/firmware/acpi/platform_profile", - "ACPI Platform Profile Control", - ); - check_and_print_sysfs_path("/sys/class/power_supply", "Power Supply Information"); - - println!("\n--- CPU INFORMATION ---"); - println!("Current Governor: {:?}", report.cpu_global.current_governor); - println!( - "Available Governors: {}", - report.cpu_global.available_governors.join(", ") - ); - println!("Turbo Status: {:?}", report.cpu_global.turbo_status); - println!( - "Energy Performance Preference (EPP): {:?}", - report.cpu_global.epp - ); - println!("Energy Performance Bias (EPB): {:?}", report.cpu_global.epb); - - // Add governor override information - if let Some(override_governor) = cpu::get_governor_override() { - println!("Governor Override: {}", override_governor.trim()); - } else { - println!("Governor Override: None"); - } - - println!("\n--- PLATFORM PROFILE ---"); - println!( - "Current Platform Profile: {:?}", - report.cpu_global.platform_profile - ); - match cpu::get_platform_profiles() { - Ok(profiles) => println!("Available Platform Profiles: {}", profiles.join(", ")), - Err(_) => println!("Available Platform Profiles: Not supported on this system"), - } - - println!("\n--- CPU CORES DETAIL ---"); - println!("Total CPU Cores: {}", report.cpu_cores.len()); - for core in &report.cpu_cores { - println!("Core {}:", core.core_id); - println!( - " Current Frequency: {} MHz", - core.current_frequency_mhz - .map_or_else(|| "N/A".to_string(), |f| f.to_string()) - ); - println!( - " Min Frequency: {} MHz", - core.min_frequency_mhz - .map_or_else(|| "N/A".to_string(), |f| f.to_string()) - ); - println!( - " Max Frequency: {} MHz", - core.max_frequency_mhz - .map_or_else(|| "N/A".to_string(), |f| f.to_string()) - ); - println!( - " Usage: {}%", - core.usage_percent - .map_or_else(|| "N/A".to_string(), |u| format!("{u:.1}")) - ); - println!( - " Temperature: {}°C", - core.temperature_celsius - .map_or_else(|| "N/A".to_string(), |t| format!("{t:.1}")) - ); - } - - println!("\n--- TEMPERATURE INFORMATION ---"); - println!( - "Average CPU Temperature: {}", - report.cpu_global.average_temperature_celsius.map_or_else( - || "N/A (CPU temperature sensor not detected)".to_string(), - |t| format!("{t:.1}°C") - ) - ); - - println!("\n--- BATTERY INFORMATION ---"); - if report.batteries.is_empty() { - println!("No batteries found or all are ignored."); - } else { - for battery in &report.batteries { - println!("Battery: {}", battery.name); - println!(" AC Connected: {}", battery.ac_connected); - println!( - " Charging State: {}", - battery.charging_state.as_deref().unwrap_or("N/A") - ); - println!( - " Capacity: {}%", - battery - .capacity_percent - .map_or_else(|| "N/A".to_string(), |c| c.to_string()) - ); - println!( - " Power Rate: {} W", - battery - .power_rate_watts - .map_or_else(|| "N/A".to_string(), |p| format!("{p:.2}")) - ); - println!( - " Charge Start Threshold: {}", - battery - .charge_start_threshold - .map_or_else(|| "N/A".to_string(), |t| t.to_string()) - ); - println!( - " Charge Stop Threshold: {}", - battery - .charge_stop_threshold - .map_or_else(|| "N/A".to_string(), |t| t.to_string()) - ); - } - } - - println!("\n--- SYSTEM LOAD ---"); - println!( - "Load Average (1 min): {:.2}", - report.system_load.load_avg_1min - ); - println!( - "Load Average (5 min): {:.2}", - report.system_load.load_avg_5min - ); - println!( - "Load Average (15 min): {:.2}", - report.system_load.load_avg_15min - ); - - println!("\n--- DAEMON STATUS ---"); - // Simple check for daemon status - can be expanded later - let daemon_status = fs::metadata("/var/run/superfreq.pid").is_ok(); - println!("Daemon Running: {daemon_status}"); - - // Check for systemd service status - if let Ok(systemd_status) = is_systemd_service_active("superfreq") { - println!("Systemd Service Active: {systemd_status}"); - } - - Ok(()) - } - Err(e) => Err(AppError::Monitor(e)), - } -} - -/// Get kernel version information -fn get_kernel_info() -> Result { - let output = Command::new("uname") - .arg("-r") - .output() - .map_err(AppError::Io)?; - - let kernel_version = String::from_utf8(output.stdout) - .map_err(|e| AppError::Generic(format!("Failed to parse kernel version: {e}")))?; - Ok(kernel_version.trim().to_string()) -} - -/// Get system uptime -fn get_system_uptime() -> Result { - let uptime_str = fs::read_to_string("/proc/uptime").map_err(AppError::Io)?; - let uptime_secs = uptime_str - .split_whitespace() - .next() - .ok_or_else(|| AppError::Generic("Invalid format in /proc/uptime file".to_string()))? - .parse::() - .map_err(|e| AppError::Generic(format!("Failed to parse uptime from /proc/uptime: {e}")))?; - - Ok(Duration::from_secs_f64(uptime_secs)) -} - -/// Check if a sysfs path exists and print its status -fn check_and_print_sysfs_path(path: &str, description: &str) { - let exists = std::path::Path::new(path).exists(); - println!( - "{}: {} ({})", - description, - path, - if exists { "Exists" } else { "Not Found" } - ); -} - -/// Check if a systemd service is active -fn is_systemd_service_active(service_name: &str) -> Result { - let output = Command::new("systemctl") - .arg("is-active") - .arg(format!("{service_name}.service")) - .stdout(Stdio::piped()) // capture stdout instead of letting it print - .stderr(Stdio::null()) // redirect stderr to null - .output() - .map_err(AppError::Io)?; - - // Check if the command executed successfully - if !output.status.success() { - // Command failed - service is either not found or not active - return Ok(false); - } - - // Command executed successfully, now check the output content - let status = String::from_utf8(output.stdout) - .map_err(|e| AppError::Generic(format!("Failed to parse systemctl output: {e}")))?; - - // Explicitly verify the output is "active" - Ok(status.trim() == "active") -} diff --git a/src/cli/mod.rs b/src/cli/mod.rs deleted file mode 100644 index 2f36523..0000000 --- a/src/cli/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod debug; diff --git a/src/config/load.rs b/src/config/load.rs index 51f7e22..15f4248 100644 --- a/src/config/load.rs +++ b/src/config/load.rs @@ -2,7 +2,9 @@ use std::fs; use std::path::{Path, PathBuf}; -use crate::config::types::{AppConfig, AppConfigToml, ConfigError, DaemonConfig, ProfileConfig}; +use anyhow::Context as _; + +use crate::config::types::{AppConfig, AppConfigToml, DaemonConfig, ProfileConfig}; /// The primary function to load application configuration from a specific path or from default locations. /// @@ -14,22 +16,23 @@ use crate::config::types::{AppConfig, AppConfigToml, ConfigError, DaemonConfig, /// /// * `Ok(AppConfig)` - Successfully loaded configuration /// * `Err(ConfigError)` - Error loading or parsing configuration -pub fn load_config() -> Result { +pub fn load_config() -> anyhow::Result { load_config_from_path(None) } /// Load configuration from a specific path or try default paths -pub fn load_config_from_path(specific_path: Option<&str>) -> Result { +pub fn load_config_from_path(specific_path: Option<&str>) -> anyhow::Result { // If a specific path is provided, only try that one if let Some(path_str) = specific_path { let path = Path::new(path_str); if path.exists() { return load_and_parse_config(path); } - return Err(ConfigError::Io(std::io::Error::new( + + Err(std::io::Error::new( std::io::ErrorKind::NotFound, format!("Specified config file not found: {}", path.display()), - ))); + ))?; } // Check for SUPERFREQ_CONFIG environment variable @@ -79,10 +82,16 @@ pub fn load_config_from_path(specific_path: Option<&str>) -> Result Result { - let contents = fs::read_to_string(path).map_err(ConfigError::Io)?; +fn load_and_parse_config(path: &Path) -> anyhow::Result { + let contents = fs::read_to_string(path).with_context(|| { + format!( + "failed to read config file from '{path}'", + path = path.display(), + ) + })?; - let toml_app_config = toml::from_str::(&contents).map_err(ConfigError::Toml)?; + let toml_app_config = + toml::from_str::(&contents).context("failed to parse config toml")?; // Handle inheritance of values from global to profile configs let mut charger_profile = toml_app_config.charger.clone(); diff --git a/src/config/types.rs b/src/config/types.rs index 3150fc5..c0be6e2 100644 --- a/src/config/types.rs +++ b/src/config/types.rs @@ -1,16 +1,18 @@ +use anyhow::bail; // Configuration types and structures for superfreq -use crate::core::TurboSetting; use serde::{Deserialize, Serialize}; use std::convert::TryFrom; /// Defines constant-returning functions used for default values. -/// This hopefully reduces repetition since we have way too many default functions -/// that just return constants. +/// This hopefully reduces repetition since we have way too many +/// default functions that just return constants. macro_rules! default_const { - ($name:ident, $type:ty, $value:expr) => { - const fn $name() -> $type { - $value - } + ($($name:ident -> $type:ty = $value:expr;)*) => { + $( + const fn $name() -> $type { + $value + } + )* }; } @@ -20,34 +22,21 @@ pub struct PowerSupplyChargeThresholds { pub stop: u8, } -impl PowerSupplyChargeThresholds { - pub fn new(start: u8, stop: u8) -> Result { +impl TryFrom<(u8, u8)> for PowerSupplyChargeThresholds { + type Error = anyhow::Error; + + fn try_from((start, stop): (u8, u8)) -> anyhow::Result { if stop == 0 { - return Err(ConfigError::Validation( - "Stop threshold must be greater than 0%".to_string(), - )); + bail!("stop threshold must be greater than 0%"); } if start >= stop { - return Err(ConfigError::Validation(format!( - "Start threshold ({start}) must be less than stop threshold ({stop})" - ))); + bail!("start threshold ({start}) must be less than stop threshold ({stop})"); } if stop > 100 { - return Err(ConfigError::Validation(format!( - "Stop threshold ({stop}) cannot exceed 100%" - ))); + bail!("stop threshold ({stop}) cannot exceed 100%"); } - Ok(Self { start, stop }) - } -} - -impl TryFrom<(u8, u8)> for PowerSupplyChargeThresholds { - type Error = ConfigError; - - fn try_from(values: (u8, u8)) -> Result { - let (start, stop) = values; - Self::new(start, stop) + Ok(PowerSupplyChargeThresholds { start, stop }) } } @@ -55,7 +44,7 @@ impl TryFrom<(u8, u8)> for PowerSupplyChargeThresholds { #[derive(Deserialize, Serialize, Debug, Clone)] pub struct ProfileConfig { pub governor: Option, - pub turbo: Option, + pub turbo: Option, pub epp: Option, // Energy Performance Preference (EPP) pub epb: Option, // Energy Performance Bias (EPB) - usually an integer, but string for flexibility from sysfs pub min_freq_mhz: Option, @@ -73,7 +62,7 @@ impl Default for ProfileConfig { fn default() -> Self { Self { governor: Some("schedutil".to_string()), // common sensible default (?) - turbo: Some(TurboSetting::Auto), + turbo: None, epp: None, // defaults depend on governor and system epb: None, // defaults depend on governor and system min_freq_mhz: None, // no override @@ -97,19 +86,6 @@ pub struct AppConfig { pub daemon: DaemonConfig, } -// Error type for config loading -#[derive(Debug, thiserror::Error)] -pub enum ConfigError { - #[error("I/O error: {0}")] - Io(#[from] std::io::Error), - - #[error("TOML parsing error: {0}")] - Toml(#[from] toml::de::Error), - - #[error("Configuration validation error: {0}")] - Validation(String), -} - // Intermediate structs for TOML parsing #[derive(Deserialize, Serialize, Debug, Clone)] pub struct ProfileConfigToml { @@ -178,22 +154,14 @@ pub const DEFAULT_LOAD_THRESHOLD_LOW: f32 = 30.0; // disable turbo if load is be pub const DEFAULT_TEMP_THRESHOLD_HIGH: f32 = 75.0; // disable turbo if temperature is above this pub const DEFAULT_INITIAL_TURBO_STATE: bool = false; // by default, start with turbo disabled -default_const!( - default_load_threshold_high, - f32, - DEFAULT_LOAD_THRESHOLD_HIGH -); -default_const!(default_load_threshold_low, f32, DEFAULT_LOAD_THRESHOLD_LOW); -default_const!( - default_temp_threshold_high, - f32, - DEFAULT_TEMP_THRESHOLD_HIGH -); -default_const!( - default_initial_turbo_state, - bool, - DEFAULT_INITIAL_TURBO_STATE -); +default_const! { + default_load_threshold_high -> f32 = DEFAULT_LOAD_THRESHOLD_HIGH; + default_load_threshold_low -> f32 = DEFAULT_LOAD_THRESHOLD_LOW; + + default_temp_threshold_high -> f32 = DEFAULT_TEMP_THRESHOLD_HIGH; + + default_initial_turbo_state -> bool = DEFAULT_INITIAL_TURBO_STATE; +} impl Default for TurboAutoSettings { fn default() -> Self { @@ -213,10 +181,10 @@ impl From for ProfileConfig { turbo: toml_config .turbo .and_then(|s| match s.to_lowercase().as_str() { - "always" => Some(TurboSetting::Always), - "auto" => Some(TurboSetting::Auto), - "never" => Some(TurboSetting::Never), - _ => None, + "always" => Some(true), + "auto" => None, + "never" => Some(false), + _ => panic!("invalid turbo value: {s}, must be one of: always, auto, never"), }), epp: toml_config.epp, epb: toml_config.epb, @@ -270,14 +238,16 @@ impl Default for DaemonConfig { } } -default_const!(default_poll_interval_sec, u64, 5); -default_const!(default_adaptive_interval, bool, false); -default_const!(default_min_poll_interval_sec, u64, 1); -default_const!(default_max_poll_interval_sec, u64, 30); -default_const!(default_throttle_on_battery, bool, true); -default_const!(default_log_level, LogLevel, LogLevel::Info); -default_const!(default_stats_file_path, Option, None); -default_const!(default_enable_auto_turbo, bool, true); +default_const! { + default_poll_interval_sec -> u64 = 5; + default_adaptive_interval -> bool = false; + default_min_poll_interval_sec -> u64 = 1; + default_max_poll_interval_sec -> u64 = 30; + default_throttle_on_battery -> bool = true; + default_log_level -> LogLevel = LogLevel::Info; + default_stats_file_path -> Option = None; + default_enable_auto_turbo -> bool = true; +} #[derive(Deserialize, Serialize, Debug, Clone)] pub struct DaemonConfigToml { diff --git a/src/daemon.rs b/src/daemon.rs index e2e4fb1..ba6d37d 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -1,8 +1,10 @@ -use crate::config::{AppConfig, LogLevel}; +use anyhow::Context; +use anyhow::bail; + +use crate::config::AppConfig; use crate::core::SystemReport; use crate::engine; use crate::monitor; -use crate::util::error::{AppError, ControlError}; use std::collections::VecDeque; use std::fs::File; use std::io::Write; @@ -60,10 +62,7 @@ fn idle_multiplier(idle_secs: u64) -> f32 { /// Calculate optimal polling interval based on system conditions and history /// /// Returns Ok with the calculated interval, or Err if the configuration is invalid -fn compute_new( - params: &IntervalParams, - system_history: &SystemHistory, -) -> Result { +fn compute_new(params: &IntervalParams, system_history: &SystemHistory) -> anyhow::Result { // Use the centralized validation function validate_poll_intervals(params.min_interval, params.max_interval)?; @@ -361,7 +360,7 @@ impl SystemHistory { &self, config: &AppConfig, on_battery: bool, - ) -> Result { + ) -> anyhow::Result { let params = IntervalParams { base_interval: config.daemon.poll_interval_sec, min_interval: config.daemon.min_poll_interval_sec, @@ -380,37 +379,31 @@ impl SystemHistory { /// Validates that poll interval configuration is consistent /// Returns Ok if configuration is valid, Err with a descriptive message if invalid -fn validate_poll_intervals(min_interval: u64, max_interval: u64) -> Result<(), ControlError> { +fn validate_poll_intervals(min_interval: u64, max_interval: u64) -> anyhow::Result<()> { if min_interval < 1 { - return Err(ControlError::InvalidValueError( - "min_interval must be ≥ 1".to_string(), - )); + bail!("min_interval must be ≥ 1"); } if max_interval < 1 { - return Err(ControlError::InvalidValueError( - "max_interval must be ≥ 1".to_string(), - )); + bail!("max_interval must be ≥ 1"); } if max_interval >= min_interval { Ok(()) } else { - Err(ControlError::InvalidValueError(format!( + bail!( "Invalid interval configuration: max_interval ({max_interval}) is less than min_interval ({min_interval})" - ))) + ); } } /// Run the daemon -pub fn run_daemon(config: AppConfig) -> Result<(), AppError> { +pub fn run_daemon(config: AppConfig) -> anyhow::Result<()> { log::info!("Starting superfreq daemon..."); // Validate critical configuration values before proceeding - if let Err(err) = validate_poll_intervals( + validate_poll_intervals( config.daemon.min_poll_interval_sec, config.daemon.max_poll_interval_sec, - ) { - return Err(AppError::Control(err)); - } + )?; // Create a flag that will be set to true when a signal is received let running = Arc::new(AtomicBool::new(true)); @@ -421,7 +414,7 @@ pub fn run_daemon(config: AppConfig) -> Result<(), AppError> { log::info!("Received shutdown signal, exiting..."); r.store(false, Ordering::SeqCst); }) - .map_err(|e| AppError::Generic(format!("Error setting Ctrl-C handler: {e}")))?; + .context("failed to set Ctrl-C handler")?; log::info!( "Daemon initialized with poll interval: {}s", diff --git a/src/engine.rs b/src/engine.rs index 0aa2644..6c5fe59 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,8 +1,7 @@ use crate::config::{AppConfig, ProfileConfig, TurboAutoSettings}; -use crate::core::{OperationalMode, SystemReport, TurboSetting}; +use crate::core::{OperationalMode, SystemReport}; use crate::cpu::{self}; use crate::power_supply; -use crate::util::error::{ControlError, EngineError}; use std::sync::OnceLock; use std::sync::atomic::{AtomicBool, Ordering}; @@ -119,30 +118,14 @@ impl TurboHysteresis { /// 1. Try to apply a feature setting /// 2. If not supported, log a warning and continue /// 3. If other error, propagate the error -fn try_apply_feature( +fn try_apply_feature anyhow::Result<()>, T>( feature_name: &str, value_description: &str, apply_fn: F, -) -> Result<(), EngineError> -where - F: FnOnce() -> Result, -{ +) -> anyhow::Result<()> { log::info!("Setting {feature_name} to '{value_description}'"); - match apply_fn() { - Ok(_) => Ok(()), - Err(e) => { - if matches!(e, ControlError::NotSupported(_)) { - log::warn!( - "{feature_name} setting is not supported on this system. Skipping {feature_name} configuration." - ); - Ok(()) - } else { - // Propagate all other errors, including InvalidValueError - Err(EngineError::ControlError(e)) - } - } - } + apply_fn() } /// Determines the appropriate CPU profile based on power status or forced mode, @@ -151,19 +134,19 @@ pub fn determine_and_apply_settings( report: &SystemReport, config: &AppConfig, force_mode: Option, -) -> Result<(), EngineError> { - // First, check if there's a governor override set - if let Some(override_governor) = cpu::get_governor_override() { - log::info!( - "Governor override is active: '{}'. Setting governor.", - override_governor.trim() - ); +) -> anyhow::Result<()> { + // // First, check if there's a governor override set + // if let Some(override_governor) = cpu::get_governor_override() { + // log::info!( + // "Governor override is active: '{}'. Setting governor.", + // override_governor.trim() + // ); - // Apply the override governor setting - try_apply_feature("override governor", override_governor.trim(), || { - cpu::set_governor(override_governor.trim(), None) - })?; - } + // // Apply the override governor setting + // try_apply_feature("override governor", override_governor.trim(), || { + // cpu::set_governor(override_governor.trim(), None) + // })?; + // } // Determine AC/Battery status once, early in the function // For desktops (no batteries), we should always use the AC power profile @@ -203,17 +186,11 @@ pub fn determine_and_apply_settings( // Apply settings from selected_profile_config if let Some(governor) = &selected_profile_config.governor { log::info!("Setting governor to '{governor}'"); - // Let set_governor handle the validation - if let Err(e) = cpu::set_governor(governor, None) { - // If the governor is not available, log a warning - if matches!(e, ControlError::InvalidGovernor(_)) - || matches!(e, ControlError::NotSupported(_)) - { - log::warn!( - "Configured governor '{governor}' is not available on this system. Skipping." - ); - } else { - return Err(e.into()); + for cpu in cpu::Cpu::all()? { + // Let set_governor handle the validation + if let Err(error) = cpu.set_governor(governor) { + // If the governor is not available, log a warning + log::warn!("{error}"); } } } @@ -297,7 +274,7 @@ fn manage_auto_turbo( report: &SystemReport, config: &ProfileConfig, on_ac_power: bool, -) -> Result<(), EngineError> { +) -> anyhow::Result<()> { // Get the auto turbo settings from the config let turbo_settings = &config.turbo_auto_settings; diff --git a/src/main.rs b/src/main.rs index f4a7120..18341a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -mod cli; mod config; mod core; mod cpu; @@ -6,7 +5,6 @@ mod daemon; mod engine; mod monitor; mod power_supply; -mod util; use anyhow::Context; use clap::Parser as _; @@ -162,7 +160,7 @@ fn real_main() -> anyhow::Result<()> { } => { let power_supplies = match for_ { Some(names) => { - let power_supplies = Vec::with_capacity(names.len()); + let mut power_supplies = Vec::with_capacity(names.len()); for name in names { power_supplies.push(power_supply::PowerSupply::from_name(name)?); diff --git a/src/monitor.rs b/src/monitor.rs index 5724ae6..79d2635 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -1,7 +1,5 @@ use crate::config::AppConfig; use crate::core::{BatteryInfo, CpuCoreInfo, CpuGlobalInfo, SystemInfo, SystemLoad, SystemReport}; -use crate::cpu::get_real_cpus; -use crate::util::error::SysMonitorError; use std::{ collections::HashMap, fs, @@ -12,10 +10,8 @@ use std::{ time::SystemTime, }; -pub type Result = std::result::Result; - // Read a sysfs file to a string, trimming whitespace -fn read_sysfs_file_trimmed(path: impl AsRef) -> Result { +fn read_sysfs_file_trimmed(path: impl AsRef) -> anyhow::Result { fs::read_to_string(path.as_ref()) .map(|s| s.trim().to_string()) .map_err(|e| { @@ -24,7 +20,7 @@ fn read_sysfs_file_trimmed(path: impl AsRef) -> Result { } // Read a sysfs file and parse it to a specific type -fn read_sysfs_value(path: impl AsRef) -> Result { +fn read_sysfs_value(path: impl AsRef) -> anyhow::Result { let content = read_sysfs_file_trimmed(path.as_ref())?; content.parse::().map_err(|_| { SysMonitorError::ParseError(format!( @@ -76,7 +72,7 @@ impl CpuTimes { } } -fn read_all_cpu_times() -> Result> { +fn read_all_cpu_times() -> anyhow::Result> { let content = fs::read_to_string("/proc/stat").map_err(SysMonitorError::Io)?; let mut cpu_times_map = HashMap::new(); @@ -156,7 +152,7 @@ pub fn get_cpu_core_info( core_id: u32, prev_times: &CpuTimes, current_times: &CpuTimes, -) -> Result { +) -> anyhow::Result { let cpufreq_path = PathBuf::from(format!("/sys/devices/system/cpu/cpu{core_id}/cpufreq/")); let current_frequency_mhz = read_sysfs_value::(cpufreq_path.join("scaling_cur_freq")) @@ -358,7 +354,7 @@ fn get_fallback_temperature(hw_path: &Path) -> Option { None } -pub fn get_all_cpu_core_info() -> Result> { +pub fn get_all_cpu_core_info() -> anyhow::Result> { let initial_cpu_times = read_all_cpu_times()?; thread::sleep(Duration::from_millis(250)); // interval for CPU usage calculation let final_cpu_times = read_all_cpu_times()?; @@ -486,7 +482,7 @@ pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> CpuGlobalInfo { } } -pub fn get_battery_info(config: &AppConfig) -> Result> { +pub fn get_battery_info(config: &AppConfig) -> anyhow::Result> { let mut batteries = Vec::new(); let power_supply_path = Path::new("/sys/class/power_supply"); @@ -682,7 +678,7 @@ fn is_likely_desktop_system() -> bool { true } -pub fn get_system_load() -> Result { +pub fn get_system_load() -> anyhow::Result { let loadavg_str = read_sysfs_file_trimmed("/proc/loadavg")?; let parts: Vec<&str> = loadavg_str.split_whitespace().collect(); if parts.len() < 3 { @@ -707,7 +703,7 @@ pub fn get_system_load() -> Result { }) } -pub fn collect_system_report(config: &AppConfig) -> Result { +pub fn collect_system_report(config: &AppConfig) -> anyhow::Result { let system_info = get_system_info(); let cpu_cores = get_all_cpu_core_info()?; let cpu_global = get_cpu_global_info(&cpu_cores); @@ -724,7 +720,7 @@ pub fn collect_system_report(config: &AppConfig) -> Result { }) } -pub fn get_cpu_model() -> Result { +pub fn get_cpu_model() -> anyhow::Result { let path = Path::new("/proc/cpuinfo"); let content = fs::read_to_string(path).map_err(|_| { SysMonitorError::ReadError(format!("Cannot read contents of {}.", path.display())) @@ -743,7 +739,7 @@ pub fn get_cpu_model() -> Result { )) } -pub fn get_linux_distribution() -> Result { +pub fn get_linux_distribution() -> anyhow::Result { let os_release_path = Path::new("/etc/os-release"); let content = fs::read_to_string(os_release_path).map_err(|_| { SysMonitorError::ReadError(format!( diff --git a/src/util/error.rs b/src/util/error.rs deleted file mode 100644 index b91081f..0000000 --- a/src/util/error.rs +++ /dev/null @@ -1,80 +0,0 @@ -use std::io; - -#[derive(Debug, thiserror::Error)] -pub enum ControlError { - #[error("I/O error: {0}")] - Io(#[from] io::Error), - - #[error("Failed to write to sysfs path: {0}")] - WriteError(String), - - #[error("Failed to read sysfs path: {0}")] - ReadError(String), - - #[error("Invalid value for setting: {0}")] - InvalidValueError(String), - - #[error("Control action not supported: {0}")] - NotSupported(String), - - #[error("Permission denied: {0}. Try running with sudo.")] - PermissionDenied(String), - - #[error("Invalid platform control profile {0} supplied, please provide a valid one.")] - InvalidProfile(String), - - #[error("Invalid governor: {0}")] - InvalidGovernor(String), - - #[error("Failed to parse value: {0}")] - ParseError(String), - - #[error("Path missing: {0}")] - PathMissing(String), -} - -#[derive(Debug, thiserror::Error)] -pub enum SysMonitorError { - #[error("I/O error: {0}")] - Io(#[from] io::Error), - - #[error("Failed to read sysfs path: {0}")] - ReadError(String), - - #[error("Failed to parse value: {0}")] - ParseError(String), - - #[error("Failed to parse /proc/stat: {0}")] - ProcStatParseError(String), -} - -#[derive(Debug, thiserror::Error)] -pub enum EngineError { - #[error("CPU control error: {0}")] - ControlError(#[from] ControlError), - - #[error("Configuration error: {0}")] - ConfigurationError(String), -} - -// A unified error type for the entire application -#[derive(Debug, thiserror::Error)] -pub enum AppError { - #[error("{0}")] - Control(#[from] ControlError), - - #[error("{0}")] - Monitor(#[from] SysMonitorError), - - #[error("{0}")] - Engine(#[from] EngineError), - - #[error("{0}")] - Config(#[from] crate::config::ConfigError), - - #[error("{0}")] - Generic(String), - - #[error("I/O error: {0}")] - Io(#[from] io::Error), -} diff --git a/src/util/mod.rs b/src/util/mod.rs deleted file mode 100644 index 0aa2927..0000000 --- a/src/util/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod error; -pub mod sysfs; diff --git a/src/util/sysfs.rs b/src/util/sysfs.rs deleted file mode 100644 index e1776e5..0000000 --- a/src/util/sysfs.rs +++ /dev/null @@ -1,80 +0,0 @@ -use crate::util::error::ControlError; -use std::{fs, io, path::Path}; - -/// Write a value to a sysfs file with consistent error handling -/// -/// # Arguments -/// -/// * `path` - The file path to write to -/// * `value` - The string value to write -/// -/// # Errors -/// -/// Returns a `ControlError` variant based on the specific error: -/// - `ControlError::PermissionDenied` if permission is denied -/// - `ControlError::PathMissing` if the path doesn't exist -/// - `ControlError::WriteError` for other I/O errors -pub fn write_sysfs_value(path: impl AsRef, value: &str) -> Result<(), ControlError> { - let p = path.as_ref(); - - fs::write(p, value).map_err(|e| { - let error_msg = format!("Path: {:?}, Value: '{}', Error: {}", p.display(), value, e); - match e.kind() { - io::ErrorKind::PermissionDenied => ControlError::PermissionDenied(error_msg), - io::ErrorKind::NotFound => { - ControlError::PathMissing(format!("Path '{}' does not exist", p.display())) - } - _ => ControlError::WriteError(error_msg), - } - }) -} - -/// Read a value from a sysfs file with consistent error handling -/// -/// # Arguments -/// -/// * `path` - The file path to read from -/// -/// # Returns -/// -/// Returns the trimmed contents of the file as a String -/// -/// # Errors -/// -/// Returns a `ControlError` variant based on the specific error: -/// - `ControlError::PermissionDenied` if permission is denied -/// - `ControlError::PathMissing` if the path doesn't exist -/// - `ControlError::ReadError` for other I/O errors -pub fn read_sysfs_value(path: impl AsRef) -> Result { - let p = path.as_ref(); - fs::read_to_string(p) - .map_err(|e| { - let error_msg = format!("Path: {:?}, Error: {}", p.display(), e); - match e.kind() { - io::ErrorKind::PermissionDenied => ControlError::PermissionDenied(error_msg), - io::ErrorKind::NotFound => { - ControlError::PathMissing(format!("Path '{}' does not exist", p.display())) - } - _ => ControlError::ReadError(error_msg), - } - }) - .map(|s| s.trim().to_string()) -} - -/// Safely check if a path exists and is writable -/// -/// # Arguments -/// -/// * `path` - The file path to check -/// -/// # Returns -/// -/// Returns true if the path exists and is writable, false otherwise -pub fn path_exists_and_writable(path: &Path) -> bool { - if !path.exists() { - return false; - } - - // Try to open the file with write access to verify write permission - fs::OpenOptions::new().write(true).open(path).is_ok() -}