From 0d3a88be036369ae2a1e6f77136d340def69b8f2 Mon Sep 17 00:00:00 2001 From: RGBCube Date: Mon, 19 May 2025 21:25:26 +0300 Subject: [PATCH] config: nuke old config and implement a new system --- Cargo.toml | 2 +- src/config.rs | 138 ++++++++++++++++++++++ src/config/load.rs | 128 -------------------- src/config/mod.rs | 5 - src/config/types.rs | 282 -------------------------------------------- src/cpu.rs | 4 +- src/main.rs | 73 +++--------- 7 files changed, 158 insertions(+), 474 deletions(-) create mode 100644 src/config.rs delete mode 100644 src/config/load.rs delete mode 100644 src/config/mod.rs delete mode 100644 src/config/types.rs diff --git a/Cargo.toml b/Cargo.toml index 3276b4a..287929e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ rust-version = "1.85" serde = { version = "1.0", features = ["derive"] } toml = "0.8" dirs = "6.0" -clap = { version = "4.0", features = ["derive"] } +clap = { version = "4.0", features = ["derive", "env"] } num_cpus = "1.16" ctrlc = "3.4" log = "0.4" diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..0e07031 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,138 @@ +use std::{fs, path::Path}; + +use anyhow::{Context, bail}; +use serde::{Deserialize, Serialize}; + +fn is_default(value: &T) -> bool { + *value == T::default() +} + +#[derive(Serialize, Deserialize, clap::Parser, Default, Debug, Clone, PartialEq, Eq)] +#[serde(deny_unknown_fields, default, rename_all = "kebab-case")] +pub struct CpuDelta { + /// The CPUs to apply the changes to. When unspecified, will be applied to all CPUs. + #[arg(short = 'c', long = "for")] + #[serde(rename = "for", skip_serializing_if = "is_default")] + pub for_: Option>, + + /// Set the CPU governor. + #[arg(short = 'g', long)] + #[serde(skip_serializing_if = "is_default")] + pub governor: Option, // TODO: Validate with clap for available governors. + + /// Set CPU Energy Performance Preference (EPP). Short form: --epp. + #[arg(short = 'p', long, alias = "epp")] + #[serde(skip_serializing_if = "is_default")] + pub energy_performance_preference: Option, // TODO: Validate with clap for available governors. + + /// Set CPU Energy Performance Bias (EPB). Short form: --epb. + #[arg(short = 'b', long, alias = "epb")] + #[serde(skip_serializing_if = "is_default")] + pub energy_performance_bias: Option, // TODO: Validate with clap for available governors. + + /// Set minimum CPU frequency in MHz. Short form: --freq-min. + #[arg(short = 'f', long, alias = "freq-min", value_parser = clap::value_parser!(u64).range(1..=10_000))] + #[serde(skip_serializing_if = "is_default")] + pub frequency_mhz_minimum: Option, + + /// Set maximum CPU frequency in MHz. Short form: --freq-max. + #[arg(short = 'F', long, alias = "freq-max", value_parser = clap::value_parser!(u64).range(1..=10_000))] + #[serde(skip_serializing_if = "is_default")] + pub frequency_mhz_maximum: Option, + + /// Set turbo boost behaviour. Has to be for all CPUs. + #[arg(short = 't', long, conflicts_with = "for_")] + #[serde(skip_serializing_if = "is_default")] + pub turbo: Option, +} + +#[derive(Serialize, Deserialize, clap::Parser, Default, Debug, Clone, PartialEq, Eq)] +#[serde(deny_unknown_fields, default, rename_all = "kebab-case")] +pub struct PowerDelta { + /// The power supplies to apply the changes to. When unspecified, will be applied to all power supplies. + #[arg(short = 'p', long = "for")] + #[serde(rename = "for", skip_serializing_if = "is_default")] + pub for_: Option>, + + /// Set the percentage that the power supply has to drop under for charging to start. Short form: --charge-start. + #[arg(short = 'c', long, alias = "charge-start", value_parser = clap::value_parser!(u8).range(0..=100))] + #[serde(skip_serializing_if = "is_default")] + pub charge_threshold_start: Option, + + /// Set the percentage where charging will stop. Short form: --charge-end. + #[arg(short = 'C', long, alias = "charge-end", value_parser = clap::value_parser!(u8).range(0..=100))] + #[serde(skip_serializing_if = "is_default")] + pub charge_threshold_end: Option, + + /// Set ACPI platform profile. Has to be for all power supplies. + #[arg(short = 'f', long, alias = "profile", conflicts_with = "for_")] + #[serde(skip_serializing_if = "is_default")] + pub platform_profile: Option, +} + +#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq, Hash)] +#[serde(untagged, rename_all = "kebab-case")] +pub enum Condition { + ChargeLessThan(u8), + ChargeMoreThan(u8), + + TemperatureLessThan(u8), + TemperatureMoreThan(u8), + + UtilizationLessThan(u8), + UtilizationMoreThan(u8), + + Charging, + OnBattery, + + False, + #[default] + True, + + All(Vec), + Any(Vec), + + Not(Box), +} + +#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)] +#[serde(deny_unknown_fields, rename_all = "kebab-case")] +pub struct DaemonConfigLayer { + priority: u8, + + #[serde(default, skip_serializing_if = "is_default")] + if_: Condition, + + #[serde(default, skip_serializing_if = "is_default")] + cpu: CpuDelta, + #[serde(default, skip_serializing_if = "is_default")] + power: PowerDelta, +} + +#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)] +#[serde(transparent, default, rename_all = "kebab-case")] +pub struct DaemonConfig(pub Vec); + +impl DaemonConfig { + pub fn load_from(path: &Path) -> anyhow::Result { + let contents = fs::read_to_string(path).with_context(|| { + format!("failed to read config from '{path}'", path = path.display()) + })?; + + let config: Self = toml::from_str(&contents).context("failed to parse config file")?; + + { + let mut priorities = Vec::with_capacity(config.0.len()); + + for layer in &config.0 { + if priorities.contains(&layer.priority) { + bail!("each config layer must have a different priority") + } + + priorities.push(layer.priority); + } + } + + Ok(config) + } +} diff --git a/src/config/load.rs b/src/config/load.rs deleted file mode 100644 index 15f4248..0000000 --- a/src/config/load.rs +++ /dev/null @@ -1,128 +0,0 @@ -// Configuration loading functionality -use std::fs; -use std::path::{Path, PathBuf}; - -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. -/// -/// # Arguments -/// -/// * `specific_path` - If provided, only attempts to load from this path and errors if not found -/// -/// # Returns -/// -/// * `Ok(AppConfig)` - Successfully loaded configuration -/// * `Err(ConfigError)` - Error loading or parsing configuration -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>) -> 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); - } - - Err(std::io::Error::new( - std::io::ErrorKind::NotFound, - format!("Specified config file not found: {}", path.display()), - ))?; - } - - // Check for SUPERFREQ_CONFIG environment variable - if let Ok(env_path) = std::env::var("SUPERFREQ_CONFIG") { - let env_path = Path::new(&env_path); - if env_path.exists() { - println!( - "Loading config from SUPERFREQ_CONFIG: {}", - env_path.display() - ); - return load_and_parse_config(env_path); - } - eprintln!( - "Warning: Config file specified by SUPERFREQ_CONFIG not found: {}", - env_path.display() - ); - } - - // System-wide paths - let config_paths = vec![ - PathBuf::from("/etc/xdg/superfreq/config.toml"), - PathBuf::from("/etc/superfreq.toml"), - ]; - - for path in config_paths { - if path.exists() { - println!("Loading config from: {}", path.display()); - match load_and_parse_config(&path) { - Ok(config) => return Ok(config), - Err(e) => { - eprintln!("Error with config file {}: {}", path.display(), e); - // Continue trying other files - } - } - } - } - - println!("No configuration file found or all failed to parse. Using default configuration."); - // Construct default AppConfig by converting default AppConfigToml - let default_toml_config = AppConfigToml::default(); - Ok(AppConfig { - charger: ProfileConfig::from(default_toml_config.charger), - battery: ProfileConfig::from(default_toml_config.battery), - ignored_power_supplies: default_toml_config.ignored_power_supplies, - daemon: DaemonConfig::default(), - }) -} - -/// Load and parse a configuration file -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).context("failed to parse config toml")?; - - // Handle inheritance of values from global to profile configs - let mut charger_profile = toml_app_config.charger.clone(); - let mut battery_profile = toml_app_config.battery.clone(); - - // Clone global battery_charge_thresholds once if it exists - if let Some(global_thresholds) = toml_app_config.battery_charge_thresholds { - // Apply to charger profile if not already set - if charger_profile.battery_charge_thresholds.is_none() { - charger_profile.battery_charge_thresholds = Some(global_thresholds.clone()); - } - - // Apply to battery profile if not already set - if battery_profile.battery_charge_thresholds.is_none() { - battery_profile.battery_charge_thresholds = Some(global_thresholds); - } - } - - // Convert AppConfigToml to AppConfig - Ok(AppConfig { - charger: ProfileConfig::from(charger_profile), - battery: ProfileConfig::from(battery_profile), - ignored_power_supplies: toml_app_config.ignored_power_supplies, - daemon: DaemonConfig { - poll_interval_sec: toml_app_config.daemon.poll_interval_sec, - adaptive_interval: toml_app_config.daemon.adaptive_interval, - min_poll_interval_sec: toml_app_config.daemon.min_poll_interval_sec, - max_poll_interval_sec: toml_app_config.daemon.max_poll_interval_sec, - throttle_on_battery: toml_app_config.daemon.throttle_on_battery, - log_level: toml_app_config.daemon.log_level, - stats_file_path: toml_app_config.daemon.stats_file_path, - }, - }) -} diff --git a/src/config/mod.rs b/src/config/mod.rs deleted file mode 100644 index c2f3076..0000000 --- a/src/config/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod load; -pub mod types; - -pub use load::*; -pub use types::*; diff --git a/src/config/types.rs b/src/config/types.rs deleted file mode 100644 index c0be6e2..0000000 --- a/src/config/types.rs +++ /dev/null @@ -1,282 +0,0 @@ -use anyhow::bail; -// Configuration types and structures for superfreq -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. -macro_rules! default_const { - ($($name:ident -> $type:ty = $value:expr;)*) => { - $( - const fn $name() -> $type { - $value - } - )* - }; -} - -#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)] -pub struct PowerSupplyChargeThresholds { - pub start: u8, - pub stop: u8, -} - -impl TryFrom<(u8, u8)> for PowerSupplyChargeThresholds { - type Error = anyhow::Error; - - fn try_from((start, stop): (u8, u8)) -> anyhow::Result { - if stop == 0 { - bail!("stop threshold must be greater than 0%"); - } - if start >= stop { - bail!("start threshold ({start}) must be less than stop threshold ({stop})"); - } - if stop > 100 { - bail!("stop threshold ({stop}) cannot exceed 100%"); - } - - Ok(PowerSupplyChargeThresholds { start, stop }) - } -} - -// Structs for configuration using serde::Deserialize -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct ProfileConfig { - pub governor: 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, - pub max_freq_mhz: Option, - pub platform_profile: Option, - #[serde(default)] - pub turbo_auto_settings: TurboAutoSettings, - #[serde(default)] - pub enable_auto_turbo: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub battery_charge_thresholds: Option, -} - -impl Default for ProfileConfig { - fn default() -> Self { - Self { - governor: Some("schedutil".to_string()), // common sensible default (?) - turbo: None, - epp: None, // defaults depend on governor and system - epb: None, // defaults depend on governor and system - min_freq_mhz: None, // no override - max_freq_mhz: None, // no override - platform_profile: None, // no override - turbo_auto_settings: TurboAutoSettings::default(), - enable_auto_turbo: default_enable_auto_turbo(), - battery_charge_thresholds: None, - } - } -} - -#[derive(Deserialize, Serialize, Debug, Default, Clone)] -pub struct AppConfig { - #[serde(default)] - pub charger: ProfileConfig, - #[serde(default)] - pub battery: ProfileConfig, - pub ignored_power_supplies: Option>, - #[serde(default)] - pub daemon: DaemonConfig, -} - -// Intermediate structs for TOML parsing -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct ProfileConfigToml { - pub governor: Option, - pub turbo: Option, // "always", "auto", "never" - pub epp: Option, - pub epb: Option, - pub min_freq_mhz: Option, - pub max_freq_mhz: Option, - pub platform_profile: Option, - pub turbo_auto_settings: Option, - #[serde(default = "default_enable_auto_turbo")] - pub enable_auto_turbo: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub battery_charge_thresholds: Option, -} - -#[derive(Deserialize, Serialize, Debug, Clone, Default)] -pub struct AppConfigToml { - #[serde(default)] - pub charger: ProfileConfigToml, - #[serde(default)] - pub battery: ProfileConfigToml, - #[serde(skip_serializing_if = "Option::is_none")] - pub battery_charge_thresholds: Option, - pub ignored_power_supplies: Option>, - #[serde(default)] - pub daemon: DaemonConfigToml, -} - -impl Default for ProfileConfigToml { - fn default() -> Self { - Self { - governor: Some("schedutil".to_string()), - turbo: Some("auto".to_string()), - epp: None, - epb: None, - min_freq_mhz: None, - max_freq_mhz: None, - platform_profile: None, - turbo_auto_settings: None, - enable_auto_turbo: default_enable_auto_turbo(), - battery_charge_thresholds: None, - } - } -} - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct TurboAutoSettings { - #[serde(default = "default_load_threshold_high")] - pub load_threshold_high: f32, - #[serde(default = "default_load_threshold_low")] - pub load_threshold_low: f32, - #[serde(default = "default_temp_threshold_high")] - pub temp_threshold_high: f32, - /// Initial turbo boost state when no previous state exists. - /// Set to `true` to start with turbo enabled, `false` to start with turbo disabled. - /// This is only used at first launch or after a reset. - #[serde(default = "default_initial_turbo_state")] - pub initial_turbo_state: bool, -} - -// Default thresholds for Auto turbo mode -pub const DEFAULT_LOAD_THRESHOLD_HIGH: f32 = 70.0; // enable turbo if load is above this -pub const DEFAULT_LOAD_THRESHOLD_LOW: f32 = 30.0; // disable turbo if load is below this -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_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 { - Self { - load_threshold_high: DEFAULT_LOAD_THRESHOLD_HIGH, - load_threshold_low: DEFAULT_LOAD_THRESHOLD_LOW, - temp_threshold_high: DEFAULT_TEMP_THRESHOLD_HIGH, - initial_turbo_state: DEFAULT_INITIAL_TURBO_STATE, - } - } -} - -impl From for ProfileConfig { - fn from(toml_config: ProfileConfigToml) -> Self { - Self { - governor: toml_config.governor, - turbo: toml_config - .turbo - .and_then(|s| match s.to_lowercase().as_str() { - "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, - min_freq_mhz: toml_config.min_freq_mhz, - max_freq_mhz: toml_config.max_freq_mhz, - platform_profile: toml_config.platform_profile, - turbo_auto_settings: toml_config.turbo_auto_settings.unwrap_or_default(), - enable_auto_turbo: toml_config.enable_auto_turbo, - battery_charge_thresholds: toml_config.battery_charge_thresholds, - } - } -} - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct DaemonConfig { - #[serde(default = "default_poll_interval_sec")] - pub poll_interval_sec: u64, - #[serde(default = "default_adaptive_interval")] - pub adaptive_interval: bool, - #[serde(default = "default_min_poll_interval_sec")] - pub min_poll_interval_sec: u64, - #[serde(default = "default_max_poll_interval_sec")] - pub max_poll_interval_sec: u64, - #[serde(default = "default_throttle_on_battery")] - pub throttle_on_battery: bool, - #[serde(default = "default_log_level")] - pub log_level: LogLevel, - #[serde(default = "default_stats_file_path")] - pub stats_file_path: Option, -} - -#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq)] -pub enum LogLevel { - Error, - Warning, - Info, - Debug, -} - -impl Default for DaemonConfig { - fn default() -> Self { - Self { - poll_interval_sec: default_poll_interval_sec(), - adaptive_interval: default_adaptive_interval(), - min_poll_interval_sec: default_min_poll_interval_sec(), - max_poll_interval_sec: default_max_poll_interval_sec(), - throttle_on_battery: default_throttle_on_battery(), - log_level: default_log_level(), - stats_file_path: default_stats_file_path(), - } - } -} - -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 { - #[serde(default = "default_poll_interval_sec")] - pub poll_interval_sec: u64, - #[serde(default = "default_adaptive_interval")] - pub adaptive_interval: bool, - #[serde(default = "default_min_poll_interval_sec")] - pub min_poll_interval_sec: u64, - #[serde(default = "default_max_poll_interval_sec")] - pub max_poll_interval_sec: u64, - #[serde(default = "default_throttle_on_battery")] - pub throttle_on_battery: bool, - #[serde(default = "default_log_level")] - pub log_level: LogLevel, - #[serde(default = "default_stats_file_path")] - pub stats_file_path: Option, -} - -impl Default for DaemonConfigToml { - fn default() -> Self { - Self { - poll_interval_sec: default_poll_interval_sec(), - adaptive_interval: default_adaptive_interval(), - min_poll_interval_sec: default_min_poll_interval_sec(), - max_poll_interval_sec: default_max_poll_interval_sec(), - throttle_on_battery: default_throttle_on_battery(), - log_level: default_log_level(), - stats_file_path: default_stats_file_path(), - } - } -} diff --git a/src/cpu.rs b/src/cpu.rs index 2d7a32d..d0985e9 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -14,7 +14,7 @@ fn read_u64(path: impl AsRef) -> anyhow::Result { let content = fs::read_to_string(path)?; - Ok(content.trim().parse::()?) + Ok(content.trim().parse()?) } fn write(path: impl AsRef, value: &str) -> anyhow::Result<()> { @@ -73,7 +73,7 @@ impl Cpu { }; // Has to match "cpu{N}". - let Ok(number) = cpu_prefix_removed.parse::() else { + let Ok(number) = cpu_prefix_removed.parse() else { continue; }; diff --git a/src/main.rs b/src/main.rs index 9902b79..68b929f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ use anyhow::Context; use clap::Parser as _; use std::fmt::Write as _; use std::io::Write as _; +use std::path::PathBuf; use std::{io, process}; use yansi::Paint as _; @@ -29,57 +30,17 @@ enum Command { Info, /// Start the daemon. - Start, + Start { + /// The daemon config path. + #[arg(long, env = "SUPERFREQ_CONFIG")] + config: PathBuf, + }, /// Modify CPU attributes. - CpuSet { - /// The CPUs to apply the changes to. When unspecified, will be applied to all CPUs. - #[arg(short = 'c', long = "for")] - for_: Option>, - - /// Set the CPU governor. - #[arg(short = 'g', long)] - governor: Option, // TODO: Validate with clap for available governors. - - /// Set CPU Energy Performance Preference (EPP). Short form: --epp. - #[arg(short = 'p', long, alias = "epp")] - energy_performance_preference: Option, - - /// Set CPU Energy Performance Bias (EPB). Short form: --epb. - #[arg(short = 'b', long, alias = "epb")] - energy_performance_bias: Option, - - /// Set minimum CPU frequency in MHz. Short form: --freq-min. - #[arg(short = 'f', long, alias = "freq-min", value_parser = clap::value_parser!(u64).range(1..=10_000))] - frequency_mhz_minimum: Option, - - /// Set maximum CPU frequency in MHz. Short form: --freq-max. - #[arg(short = 'F', long, alias = "freq-max", value_parser = clap::value_parser!(u64).range(1..=10_000))] - frequency_mhz_maximum: Option, - - /// Set turbo boost behaviour. Has to be for all CPUs. - #[arg(short = 't', long, conflicts_with = "for_")] - turbo: Option, - }, + CpuSet(config::CpuDelta), /// Modify power supply attributes. - PowerSet { - /// The power supplies to apply the changes to. When unspecified, will be applied to all power supplies. - #[arg(short = 'p', long = "for")] - for_: Option>, - - /// Set the percentage that the power supply has to drop under for charging to start. Short form: --charge-start. - #[arg(short = 'c', long, alias = "charge-start", value_parser = clap::value_parser!(u8).range(0..=100))] - charge_threshold_start: Option, - - /// Set the percentage where charging will stop. Short form: --charge-end. - #[arg(short = 'C', long, alias = "charge-end", value_parser = clap::value_parser!(u8).range(0..=100))] - charge_threshold_end: Option, - - /// Set ACPI platform profile. Has to be for all power supplies. - #[arg(short = 'f', long, alias = "profile", conflicts_with = "for_")] - platform_profile: Option, - }, + PowerSet(config::PowerDelta), } fn real_main() -> anyhow::Result<()> { @@ -91,17 +52,17 @@ fn real_main() -> anyhow::Result<()> { .format_module_path(false) .init(); - let config = config::load_config().context("failed to load config")?; - match cli.command { Command::Info => todo!(), - Command::Start => { - daemon::run_daemon(config)?; - Ok(()) + Command::Start { config } => { + let config = config::DaemonConfig::load_from(&config) + .context("failed to load daemon config file")?; + + daemon::run(config) } - Command::CpuSet { + Command::CpuSet(config::CpuDelta { for_, governor, energy_performance_preference, @@ -109,7 +70,7 @@ fn real_main() -> anyhow::Result<()> { frequency_mhz_minimum, frequency_mhz_maximum, turbo, - } => { + }) => { let cpus = match for_ { Some(numbers) => { let mut cpus = Vec::with_capacity(numbers.len()); @@ -152,12 +113,12 @@ fn real_main() -> anyhow::Result<()> { Ok(()) } - Command::PowerSet { + Command::PowerSet(config::PowerDelta { for_, charge_threshold_start, charge_threshold_end, platform_profile, - } => { + }) => { let power_supplies = match for_ { Some(names) => { let mut power_supplies = Vec::with_capacity(names.len());