mod error;
pub use error::{Error, Result};
use super::Address;
use std::{
cmp::Ordering,
collections::BTreeMap,
convert::TryFrom,
path::{Path, PathBuf},
};
use libmedium::units::PwmMode;
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum MultiTempFunction {
Min,
#[default]
Max,
Average,
}
#[derive(Serialize, Deserialize, Eq, PartialEq)]
#[serde(remote = "PwmMode")]
enum PwmModeDef {
Dc,
Pwm,
Automatic,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct ChannelConfig {
pub hwmon_device_path: PathBuf,
pub index: u16,
pub curve: BTreeMap<i32, u8>,
pub temps: Vec<Address>,
#[serde(default)]
pub virtual_temps: Vec<PathBuf>,
#[serde(default)]
pub mtf: MultiTempFunction,
#[serde(default = "default_min_pwm")]
pub min_pwm: u8,
#[serde(default = "default_min_start")]
pub min_start: u8,
#[serde(default, with = "PwmModeDef")]
pub pwm_mode: PwmMode,
#[serde(default = "default_average")]
pub average: u8,
}
impl ChannelConfig {
pub fn new(
hwmon_device_path: impl Into<PathBuf>,
index: u16,
curve: BTreeMap<i32, u8>,
temps: Vec<Address>,
virtual_temps: Vec<PathBuf>,
) -> Self {
ChannelConfig {
hwmon_device_path: hwmon_device_path.into(),
index,
curve,
temps,
virtual_temps,
mtf: Default::default(),
min_pwm: default_min_pwm(),
min_start: default_min_start(),
pwm_mode: Default::default(),
average: default_average(),
}
}
pub fn check(&self) -> Vec<Error> {
let mut errors = Vec::new();
if self.temps.is_empty() && self.virtual_temps.is_empty() {
errors.push(Error::new_empty_temps(self.address()));
}
if self.index == 0 {
errors.push(Error::new_index(self.address()));
}
if self.average < 1 || self.average > 10 {
errors.push(Error::new_average(self.address()));
}
if self.curve.is_empty() {
errors.push(Error::new_empty_curve(self.address()));
};
errors
}
pub fn sort(&mut self) {
self.temps.sort();
self.virtual_temps.sort();
}
pub fn address(&self) -> Address {
Address::new(&self.hwmon_device_path, self.index)
}
}
impl Ord for ChannelConfig {
fn cmp(&self, other: &Self) -> Ordering {
self.hwmon_device_path
.cmp(&other.hwmon_device_path)
.then(self.index.cmp(&other.index))
}
}
impl PartialOrd for ChannelConfig {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
fn default_min_start() -> u8 {
255
}
fn default_min_pwm() -> u8 {
100
}
fn default_average() -> u8 {
1
}
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
pub struct Config {
channels: Vec<ChannelConfig>,
}
impl Config {
#[cfg(not(feature = "async"))]
pub fn read(path: impl AsRef<Path>) -> Result<Config> {
let path = path.as_ref();
let config = std::fs::read_to_string(path).map_err(|e| Error::new_load(path, e))?;
Self::parse(config)
}
#[cfg(feature = "async")]
pub async fn read(path: impl AsRef<Path>) -> Result<Config> {
let path = path.as_ref();
let config = tokio::fs::read_to_string(path)
.await
.map_err(|e| Error::new_load(path, e))?;
Self::parse(config)
}
pub fn parse(config: impl AsRef<str>) -> Result<Config> {
let mut config: Config = serde_json::from_str(config.as_ref())?;
let errors = config.check();
if errors.len() > 0 {
return Err(Error::from_vec(errors).unwrap());
}
config.sort();
config
.channels
.dedup_by(|a, b| a.hwmon_device_path == b.hwmon_device_path && a.index == b.index);
Ok(config)
}
pub fn as_string(&self) -> String {
serde_json::to_string_pretty(self).unwrap_or_default()
}
pub fn channel_configs(&self) -> &[ChannelConfig] {
&self.channels
}
pub fn check(&self) -> Vec<Error> {
self.channels.iter().fold(Vec::new(), |mut err, channel| {
let errors = channel.check();
err.extend(errors);
err
})
}
pub fn sort(&mut self) {
self.channels.sort();
for channel in &mut self.channels {
channel.sort();
}
}
}
impl From<Vec<ChannelConfig>> for Config {
fn from(mut channel_configs: Vec<ChannelConfig>) -> Self {
channel_configs.sort();
Self {
channels: channel_configs,
}
}
}
impl TryFrom<&str> for Config {
type Error = Error;
fn try_from(config: &str) -> Result<Self> {
Config::parse(config)
}
}
impl TryFrom<String> for Config {
type Error = Error;
fn try_from(config: String) -> Result<Self> {
Config::parse(config)
}
}