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 yansi::Paint as _;
use std::{fmt, fs, path::Path, string::ToString};
use std::{fmt, string::ToString};
fn exists(path: impl AsRef<Path>) -> 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<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(),
)
})
}
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<String> {
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<String> {
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,
)

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 core;
mod cpu;
mod daemon;
// mod engine;
// mod monitor;
mod power_supply;
use anyhow::Context;
use clap::Parser as _;

View file

@ -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<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(),
)
})
}
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<String> {
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<String> {
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")
}
}

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<()> {}
}