diff --git a/src/cpu.rs b/src/cpu.rs index 17a5da1..7b8ef99 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -1,33 +1,9 @@ use anyhow::{Context, bail}; use yansi::Paint as _; -use std::{fmt, fs, path::Path, string::ToString}; +use std::{fmt, string::ToString}; -fn exists(path: impl AsRef) -> bool { - let path = path.as_ref(); - - path.exists() -} - -// Not doing any anyhow stuff here as all the calls of this ignore errors. -fn read_u64(path: impl AsRef) -> anyhow::Result { - let path = path.as_ref(); - - let content = fs::read_to_string(path)?; - - Ok(content.trim().parse()?) -} - -fn write(path: impl AsRef, value: &str) -> anyhow::Result<()> { - let path = path.as_ref(); - - fs::write(path, value).with_context(|| { - format!( - "failed to write '{value}' to '{path}'", - path = path.display(), - ) - }) -} +use crate::fs; #[derive(Debug, Clone, Copy)] pub struct Cpu { @@ -96,11 +72,11 @@ impl Cpu { pub fn rescan(&mut self) -> anyhow::Result<()> { let Self { number, .. } = self; - if !exists(format!("/sys/devices/system/cpu/cpu{number}")) { + if !fs::exists(format!("/sys/devices/system/cpu/cpu{number}")) { bail!("{self} does not exist"); } - let has_cpufreq = exists(format!("/sys/devices/system/cpu/cpu{number}/cpufreq")); + let has_cpufreq = fs::exists(format!("/sys/devices/system/cpu/cpu{number}/cpufreq")); self.has_cpufreq = has_cpufreq; @@ -110,7 +86,7 @@ impl Cpu { pub fn get_available_governors(&self) -> Vec { let Self { number, .. } = self; - let Ok(content) = fs::read_to_string(format!( + let Ok(Some(content)) = fs::read(format!( "/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_available_governors" )) else { return Vec::new(); @@ -137,7 +113,7 @@ impl Cpu { ); } - write( + fs::write( format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_governor"), governor, ) @@ -151,7 +127,7 @@ impl Cpu { pub fn get_available_epps(&self) -> Vec { let Self { number, .. } = self; - let Ok(content) = fs::read_to_string(format!( + let Ok(Some(content)) = fs::read(format!( "/sys/devices/system/cpu/cpu{number}/cpufreq/energy_performance_available_preferences" )) else { return Vec::new(); @@ -175,7 +151,7 @@ impl Cpu { ); } - write( + fs::write( format!("/sys/devices/system/cpu/cpu{number}/cpufreq/energy_performance_preference"), epp, ) @@ -226,7 +202,7 @@ impl Cpu { ); } - write( + fs::write( format!("/sys/devices/system/cpu/cpu{number}/cpufreq/energy_performance_bias"), epb, ) @@ -244,7 +220,7 @@ impl Cpu { let frequency_khz = frequency_mhz * 1000; let frequency_khz = frequency_khz.to_string(); - write( + fs::write( format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_min_freq"), &frequency_khz, ) @@ -256,7 +232,7 @@ impl Cpu { fn validate_frequency_minimum(&self, new_frequency_mhz: u64) -> anyhow::Result<()> { let Self { number, .. } = self; - let Ok(minimum_frequency_khz) = read_u64(format!( + let Ok(minimum_frequency_khz) = fs::read_u64(format!( "/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_min_freq" )) else { // Just let it pass if we can't find anything. @@ -282,7 +258,7 @@ impl Cpu { let frequency_khz = frequency_mhz * 1000; let frequency_khz = frequency_khz.to_string(); - write( + fs::write( format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_max_freq"), &frequency_khz, ) @@ -294,7 +270,7 @@ impl Cpu { fn validate_frequency_maximum(&self, new_frequency_mhz: u64) -> anyhow::Result<()> { let Self { number, .. } = self; - let Ok(maximum_frequency_khz) = read_u64(format!( + let Ok(maximum_frequency_khz) = fs::read_u64(format!( "/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_min_freq" )) else { // Just let it pass if we can't find anything. @@ -331,16 +307,16 @@ impl Cpu { let generic_boost_path = "/sys/devices/system/cpu/cpufreq/boost"; // Try each boost control path in order of specificity - if write(intel_boost_path_negated, value_boost_negated).is_ok() { + if fs::write(intel_boost_path_negated, value_boost_negated).is_ok() { return Ok(()); } - if write(amd_boost_path, value_boost).is_ok() { + if fs::write(amd_boost_path, value_boost).is_ok() { return Ok(()); } - if write(msr_boost_path, value_boost).is_ok() { + if fs::write(msr_boost_path, value_boost).is_ok() { return Ok(()); } - if write(generic_boost_path, value_boost).is_ok() { + if fs::write(generic_boost_path, value_boost).is_ok() { return Ok(()); } @@ -348,7 +324,7 @@ impl Cpu { if Self::all()?.iter().any(|cpu| { let Cpu { number, .. } = cpu; - write( + fs::write( format!("/sys/devices/system/cpu/cpu{number}/cpufreq/boost"), value_boost, ) diff --git a/src/fs.rs b/src/fs.rs new file mode 100644 index 0000000..f3eeb2c --- /dev/null +++ b/src/fs.rs @@ -0,0 +1,55 @@ +use std::{fs, io, path::Path}; + +use anyhow::Context; + +pub fn exists(path: impl AsRef) -> bool { + let path = path.as_ref(); + + path.exists() +} + +pub fn read_dir(path: impl AsRef) -> anyhow::Result { + let path = path.as_ref(); + + fs::read_dir(path) + .with_context(|| format!("failed to read directory '{path}'", path = path.display())) +} + +pub fn read(path: impl AsRef) -> anyhow::Result> { + let path = path.as_ref(); + + match fs::read_to_string(path) { + Ok(string) => Ok(Some(string)), + + Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(None), + + Err(error) => { + Err(error).with_context(|| format!("failed to read '{path}", path = path.display())) + } + } +} + +pub fn read_u64(path: impl AsRef) -> anyhow::Result { + let path = path.as_ref(); + + let content = fs::read_to_string(path) + .with_context(|| format!("failed to read '{path}'", path = path.display()))?; + + Ok(content.trim().parse().with_context(|| { + format!( + "failed to parse contents of '{path}' as a unsigned number", + path = path.display(), + ) + })?) +} + +pub fn write(path: impl AsRef, value: &str) -> anyhow::Result<()> { + let path = path.as_ref(); + + fs::write(path, value).with_context(|| { + format!( + "failed to write '{value}' to '{path}'", + path = path.display(), + ) + }) +} diff --git a/src/main.rs b/src/main.rs index 0725e38..825465d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,14 @@ +mod cpu; +mod power_supply; +mod system; + +mod fs; + mod config; // mod core; -mod cpu; mod daemon; // mod engine; // mod monitor; -mod power_supply; use anyhow::Context; use clap::Parser as _; diff --git a/src/power_supply.rs b/src/power_supply.rs index 1f69a3c..4e66bec 100644 --- a/src/power_supply.rs +++ b/src/power_supply.rs @@ -1,22 +1,12 @@ -use anyhow::{Context, bail}; +use anyhow::{Context, anyhow, bail}; use yansi::Paint as _; use std::{ - fmt, fs, + fmt, path::{Path, PathBuf}, }; -// TODO: Migrate to central utils file. Same exists in cpu.rs. -fn write(path: impl AsRef, value: &str) -> anyhow::Result<()> { - let path = path.as_ref(); - - fs::write(path, value).with_context(|| { - format!( - "failed to write '{value}' to '{path}'", - path = path.display(), - ) - }) -} +use crate::fs; /// Represents a pattern of path suffixes used to control charge thresholds /// for different device vendors. @@ -136,10 +126,10 @@ impl PowerSupply { fn get_type(&self) -> anyhow::Result { let type_path = self.path.join("type"); - let type_ = fs::read_to_string(&type_path) + let type_ = fs::read(&type_path) .with_context(|| format!("failed to read '{path}'", path = type_path.display()))?; - Ok(type_) + type_.ok_or_else(|| anyhow!("'{path}' doesn't exist", path = type_path.display())) } pub fn rescan(&mut self) -> anyhow::Result<()> { @@ -180,9 +170,9 @@ impl PowerSupply { } pub fn set_charge_threshold_start(&self, charge_threshold_start: u8) -> anyhow::Result<()> { - write( + fs::write( &self.charge_threshold_path_start().ok_or_else(|| { - anyhow::anyhow!( + anyhow!( "power supply '{name}' does not support changing charge threshold levels", name = self.name, ) @@ -197,9 +187,9 @@ impl PowerSupply { } pub fn set_charge_threshold_end(&self, charge_threshold_end: u8) -> anyhow::Result<()> { - write( + fs::write( &self.charge_threshold_path_end().ok_or_else(|| { - anyhow::anyhow!( + anyhow!( "power supply '{name}' does not support changing charge threshold levels", name = self.name, ) @@ -216,7 +206,7 @@ impl PowerSupply { pub fn get_available_platform_profiles() -> Vec { let path = "/sys/firmware/acpi/platform_profile_choices"; - let Ok(content) = fs::read_to_string(path) else { + let Ok(Some(content)) = fs::read(path) else { return Vec::new(); }; @@ -245,7 +235,7 @@ impl PowerSupply { ); } - write("/sys/firmware/acpi/platform_profile", profile) + fs::write("/sys/firmware/acpi/platform_profile", profile) .context("this probably means that your system does not support changing ACPI profiles") } } diff --git a/src/system.rs b/src/system.rs new file mode 100644 index 0000000..1d3e697 --- /dev/null +++ b/src/system.rs @@ -0,0 +1,14 @@ +pub struct System { + pub is_desktop: bool, +} + +impl System { + pub fn new() -> anyhow::Result { + let mut system = Self { is_desktop: false }; + system.rescan()?; + + Ok(system) + } + + pub fn rescan(&mut self) -> anyhow::Result<()> {} +}