1
Fork 0
mirror of https://github.com/RGBCube/superfreq synced 2025-07-27 17:07:44 +00:00

cpu&power: share fs impls

This commit is contained in:
RGBCube 2025-05-20 19:10:10 +03:00
parent c062327457
commit 91cef3b8b1
Signed by: RGBCube
SSH key fingerprint: SHA256:CzqbPcfwt+GxFYNnFVCqoN5Itn4YFrshg1TrnACpA5M
5 changed files with 104 additions and 65 deletions

View file

@ -1,33 +1,9 @@
use anyhow::{Context, bail}; use anyhow::{Context, bail};
use yansi::Paint as _; use yansi::Paint as _;
use std::{fmt, fs, path::Path, string::ToString}; use std::{fmt, string::ToString};
fn exists(path: impl AsRef<Path>) -> bool { use crate::fs;
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<Path>) -> anyhow::Result<u64> {
let path = path.as_ref();
let content = fs::read_to_string(path)?;
Ok(content.trim().parse()?)
}
fn write(path: impl AsRef<Path>, 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(),
)
})
}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Cpu { pub struct Cpu {
@ -96,11 +72,11 @@ impl Cpu {
pub fn rescan(&mut self) -> anyhow::Result<()> { pub fn rescan(&mut self) -> anyhow::Result<()> {
let Self { number, .. } = self; 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"); 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; self.has_cpufreq = has_cpufreq;
@ -110,7 +86,7 @@ impl Cpu {
pub fn get_available_governors(&self) -> Vec<String> { pub fn get_available_governors(&self) -> Vec<String> {
let Self { number, .. } = self; 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" "/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_available_governors"
)) else { )) else {
return Vec::new(); return Vec::new();
@ -137,7 +113,7 @@ impl Cpu {
); );
} }
write( fs::write(
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_governor"), format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_governor"),
governor, governor,
) )
@ -151,7 +127,7 @@ impl Cpu {
pub fn get_available_epps(&self) -> Vec<String> { pub fn get_available_epps(&self) -> Vec<String> {
let Self { number, .. } = self; 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" "/sys/devices/system/cpu/cpu{number}/cpufreq/energy_performance_available_preferences"
)) else { )) else {
return Vec::new(); return Vec::new();
@ -175,7 +151,7 @@ impl Cpu {
); );
} }
write( fs::write(
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/energy_performance_preference"), format!("/sys/devices/system/cpu/cpu{number}/cpufreq/energy_performance_preference"),
epp, epp,
) )
@ -226,7 +202,7 @@ impl Cpu {
); );
} }
write( fs::write(
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/energy_performance_bias"), format!("/sys/devices/system/cpu/cpu{number}/cpufreq/energy_performance_bias"),
epb, epb,
) )
@ -244,7 +220,7 @@ impl Cpu {
let frequency_khz = frequency_mhz * 1000; let frequency_khz = frequency_mhz * 1000;
let frequency_khz = frequency_khz.to_string(); let frequency_khz = frequency_khz.to_string();
write( fs::write(
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_min_freq"), format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_min_freq"),
&frequency_khz, &frequency_khz,
) )
@ -256,7 +232,7 @@ impl Cpu {
fn validate_frequency_minimum(&self, new_frequency_mhz: u64) -> anyhow::Result<()> { fn validate_frequency_minimum(&self, new_frequency_mhz: u64) -> anyhow::Result<()> {
let Self { number, .. } = self; 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" "/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_min_freq"
)) else { )) else {
// Just let it pass if we can't find anything. // 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_mhz * 1000;
let frequency_khz = frequency_khz.to_string(); let frequency_khz = frequency_khz.to_string();
write( fs::write(
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_max_freq"), format!("/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_max_freq"),
&frequency_khz, &frequency_khz,
) )
@ -294,7 +270,7 @@ impl Cpu {
fn validate_frequency_maximum(&self, new_frequency_mhz: u64) -> anyhow::Result<()> { fn validate_frequency_maximum(&self, new_frequency_mhz: u64) -> anyhow::Result<()> {
let Self { number, .. } = self; 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" "/sys/devices/system/cpu/cpu{number}/cpufreq/scaling_min_freq"
)) else { )) else {
// Just let it pass if we can't find anything. // 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"; let generic_boost_path = "/sys/devices/system/cpu/cpufreq/boost";
// Try each boost control path in order of specificity // 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(()); return Ok(());
} }
if write(amd_boost_path, value_boost).is_ok() { if fs::write(amd_boost_path, value_boost).is_ok() {
return Ok(()); return Ok(());
} }
if write(msr_boost_path, value_boost).is_ok() { if fs::write(msr_boost_path, value_boost).is_ok() {
return Ok(()); return Ok(());
} }
if write(generic_boost_path, value_boost).is_ok() { if fs::write(generic_boost_path, value_boost).is_ok() {
return Ok(()); return Ok(());
} }
@ -348,7 +324,7 @@ impl Cpu {
if Self::all()?.iter().any(|cpu| { if Self::all()?.iter().any(|cpu| {
let Cpu { number, .. } = cpu; let Cpu { number, .. } = cpu;
write( fs::write(
format!("/sys/devices/system/cpu/cpu{number}/cpufreq/boost"), format!("/sys/devices/system/cpu/cpu{number}/cpufreq/boost"),
value_boost, value_boost,
) )

55
src/fs.rs Normal file
View file

@ -0,0 +1,55 @@
use std::{fs, io, path::Path};
use anyhow::Context;
pub fn exists(path: impl AsRef<Path>) -> bool {
let path = path.as_ref();
path.exists()
}
pub fn read_dir(path: impl AsRef<Path>) -> anyhow::Result<fs::ReadDir> {
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<Path>) -> anyhow::Result<Option<String>> {
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<Path>) -> anyhow::Result<u64> {
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<Path>, 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(),
)
})
}

View file

@ -1,10 +1,14 @@
mod cpu;
mod power_supply;
mod system;
mod fs;
mod config; mod config;
// mod core; // mod core;
mod cpu;
mod daemon; mod daemon;
// mod engine; // mod engine;
// mod monitor; // mod monitor;
mod power_supply;
use anyhow::Context; use anyhow::Context;
use clap::Parser as _; use clap::Parser as _;

View file

@ -1,22 +1,12 @@
use anyhow::{Context, bail}; use anyhow::{Context, anyhow, bail};
use yansi::Paint as _; use yansi::Paint as _;
use std::{ use std::{
fmt, fs, fmt,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
// TODO: Migrate to central utils file. Same exists in cpu.rs. use crate::fs;
fn write(path: impl AsRef<Path>, 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(),
)
})
}
/// Represents a pattern of path suffixes used to control charge thresholds /// Represents a pattern of path suffixes used to control charge thresholds
/// for different device vendors. /// for different device vendors.
@ -136,10 +126,10 @@ impl PowerSupply {
fn get_type(&self) -> anyhow::Result<String> { fn get_type(&self) -> anyhow::Result<String> {
let type_path = self.path.join("type"); 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()))?; .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<()> { 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<()> { pub fn set_charge_threshold_start(&self, charge_threshold_start: u8) -> anyhow::Result<()> {
write( fs::write(
&self.charge_threshold_path_start().ok_or_else(|| { &self.charge_threshold_path_start().ok_or_else(|| {
anyhow::anyhow!( anyhow!(
"power supply '{name}' does not support changing charge threshold levels", "power supply '{name}' does not support changing charge threshold levels",
name = self.name, name = self.name,
) )
@ -197,9 +187,9 @@ impl PowerSupply {
} }
pub fn set_charge_threshold_end(&self, charge_threshold_end: u8) -> anyhow::Result<()> { pub fn set_charge_threshold_end(&self, charge_threshold_end: u8) -> anyhow::Result<()> {
write( fs::write(
&self.charge_threshold_path_end().ok_or_else(|| { &self.charge_threshold_path_end().ok_or_else(|| {
anyhow::anyhow!( anyhow!(
"power supply '{name}' does not support changing charge threshold levels", "power supply '{name}' does not support changing charge threshold levels",
name = self.name, name = self.name,
) )
@ -216,7 +206,7 @@ impl PowerSupply {
pub fn get_available_platform_profiles() -> Vec<String> { pub fn get_available_platform_profiles() -> Vec<String> {
let path = "/sys/firmware/acpi/platform_profile_choices"; 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(); 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") .context("this probably means that your system does not support changing ACPI profiles")
} }
} }

14
src/system.rs Normal file
View file

@ -0,0 +1,14 @@
pub struct System {
pub is_desktop: bool,
}
impl System {
pub fn new() -> anyhow::Result<Self> {
let mut system = Self { is_desktop: false };
system.rescan()?;
Ok(system)
}
pub fn rescan(&mut self) -> anyhow::Result<()> {}
}