Fix capabilities logic errors
This commit is contained in:
parent
908df9ec7d
commit
fe1f031c33
5 changed files with 116 additions and 15 deletions
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "sysfuss"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
|
||||
description = "sysfs wrapper for convenience"
|
||||
|
|
47
src/basic.rs
47
src/basic.rs
|
@ -20,9 +20,8 @@ impl BasicEntityPath {
|
|||
|
||||
pub(crate) fn all(root: impl AsRef<Path>, class: impl AsRef<Path>) -> IoResult<impl Iterator<Item=IoResult<Self>>> {
|
||||
let dir_path = root.as_ref().join(SYS_CLASS_PATH).join(class);
|
||||
dir_path.read_dir()
|
||||
.map(
|
||||
|iter| iter.filter_map(
|
||||
Ok(dir_path.read_dir()?
|
||||
.filter_map(
|
||||
|entry| entry.map(
|
||||
|entry| if entry.path().is_file() { None } else { Some(Self::new(entry.path())) }
|
||||
).transpose()))
|
||||
|
@ -77,6 +76,7 @@ impl crate::SysEntityAttributes<String> for BasicEntityPath {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::SysEntity;
|
||||
use crate::SysEntityRawExt;
|
||||
|
||||
#[test]
|
||||
|
@ -89,4 +89,45 @@ mod tests {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_capabilities() -> std::io::Result<()> {
|
||||
let sys = crate::SysPath::default();
|
||||
if std::fs::read_to_string("/sys/class/hwmon/hwmon0/name")?.trim() != "nvme" {
|
||||
// skip if system has different hwmons
|
||||
// TODO account for different test systems
|
||||
eprintln!("basic entity test skipped since hwmon0 is not nvme (maybe running on a different PC?)");
|
||||
return Ok(())
|
||||
}
|
||||
let basic = sys.class("hwmon", crate::capability::attributes([
|
||||
"name".to_string(),
|
||||
"temp1_alarm".to_string(),
|
||||
"temp1_crit".to_string(),
|
||||
].into_iter()))?.next().expect("Missing any hwmon");
|
||||
let attr_value = crate::SysEntityAttributesExt::attribute::<String, _>(&basic, "name".to_owned()).expect("name capable but also incapable");
|
||||
println!("Attribute ./name = '{}'", attr_value);
|
||||
assert!(attr_value == "nvme");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_drm_capabilities() -> std::io::Result<()> {
|
||||
let sys = crate::SysPath::default();
|
||||
let expected_dev = if let Ok(dev) = std::fs::read_to_string("/sys/class/drm/card1/dev") {
|
||||
dev.trim().to_owned()
|
||||
} else {
|
||||
// skip if system has different hwmons
|
||||
// TODO account for different test systems
|
||||
eprintln!("basic entity test skipped since drm card0 does not exist (maybe running on a different PC?)");
|
||||
return Ok(())
|
||||
};
|
||||
let basic = sys.class("drm", crate::capability::attributes([
|
||||
"dev".to_string(),
|
||||
"uevent".to_string(),
|
||||
].into_iter()))?.filter(|basic| basic.name().expect("no name").starts_with("card")).next().expect("Missing drm");
|
||||
let attr_value = crate::SysEntityAttributesExt::attribute::<String, _>(&basic, "dev".to_owned()).expect("dev capable but also incapable");
|
||||
println!("Attribute ./dev = '{}'", attr_value);
|
||||
assert!(attr_value == expected_dev);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,26 @@ impl <T1, T2> EitherErr2<T1, T2> {
|
|||
}
|
||||
}
|
||||
|
||||
impl <T> EitherErr2<std::convert::Infallible, T> {
|
||||
/// Convert to non-infallible error
|
||||
pub fn map_infallible_first(self) -> T {
|
||||
match self {
|
||||
Self::First(_e) => panic!("Infallible error cannot exist"),
|
||||
Self::Second(e) => e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <T> EitherErr2<T, std::convert::Infallible> {
|
||||
/// Convert to non-infallible error
|
||||
pub fn map_infallible_second(self) -> T {
|
||||
match self {
|
||||
Self::First(e) => e,
|
||||
Self::Second(_e) => panic!("Infallible error cannot exist"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <T1: std::fmt::Display, T2: std::fmt::Display> std::fmt::Display for EitherErr2<T1, T2> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
|
|
36
src/hwmon.rs
36
src/hwmon.rs
|
@ -4,6 +4,8 @@ use std::io::Result as IoResult;
|
|||
|
||||
const HWMON_DIR_PATH: &'static str = "sys/class/hwmon/";
|
||||
|
||||
use crate::SysEntityAttributesExt;
|
||||
|
||||
/// Attribute representation.
|
||||
/// Attribute files are usually in the format `<type><number>_<item>`.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
|
@ -202,7 +204,11 @@ impl HwMonPath {
|
|||
pub(crate) fn name(root: &crate::SysPath, name: &str) -> IoResult<Option<Self>> {
|
||||
for entry in Self::all(root)? {
|
||||
let entry = entry?;
|
||||
if entry.attribute(HwMonAttribute::name())? == name {
|
||||
let value: String = entry.attribute::<String, _>(HwMonAttribute::name()).map_err(|e| match e {
|
||||
crate::EitherErr2::First(e) => e,
|
||||
crate::EitherErr2::Second(_e) => panic!("Infallible"),
|
||||
})?;
|
||||
if value == name {
|
||||
return Ok(Some(entry))
|
||||
}
|
||||
}
|
||||
|
@ -215,11 +221,6 @@ impl HwMonPath {
|
|||
std::fs::read_to_string(self.path.join(name))
|
||||
}
|
||||
|
||||
/// Get a hwmon attribute (file contents)
|
||||
fn attribute(&self, attr: HwMonAttribute) -> IoResult<String> {
|
||||
self.attribute_str(&attr.to_attr_str())
|
||||
}
|
||||
|
||||
/// Get the path to a hwmon attribute.
|
||||
/// Use `HwMonPath.as_ref().join(attribute_str)` for non-standard attributes
|
||||
pub fn path_to(&self, attr: HwMonAttribute) -> PathBuf {
|
||||
|
@ -239,7 +240,7 @@ impl crate::SysEntity for HwMonPath {
|
|||
}
|
||||
|
||||
fn name(&self) -> IoResult<String> {
|
||||
self.attribute(HwMonAttribute::name())
|
||||
self.attribute(HwMonAttribute::name()).map_err(|e| e.map_infallible_second())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,10 +269,29 @@ mod tests {
|
|||
assert!(!all_hwmon.is_empty());
|
||||
for hwmon in all_hwmon.into_iter() {
|
||||
let hwmon = hwmon?;
|
||||
assert!(hwmon.attribute(HwMonAttribute::name())? != "");
|
||||
assert!(hwmon.attribute::<String, _>(HwMonAttribute::name()).map_err(|e| e.map_infallible_second())? != "");
|
||||
assert!(!hwmon.attribute::<String, _>(HwMonAttribute::name()).map_err(|e| e.map_infallible_second())?.ends_with("\n"));
|
||||
assert!(!hwmon.capabilities().is_empty());
|
||||
assert!(hwmon.capabilities().contains(&HwMonAttribute::name()))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hwmon_capabilities() -> std::io::Result<()> {
|
||||
let sys = crate::SysPath::default();
|
||||
if !sys.hwmon_by_name("amdgpu").is_ok() {
|
||||
// skip if system has no AMD GPU
|
||||
eprintln!("hwmon test skipped since amdgpu does not exist (maybe running on a laptop PC?)");
|
||||
return Ok(())
|
||||
}
|
||||
let hwmon = sys.hwmon(crate::capability::attributes([
|
||||
HwMonAttribute::name(),
|
||||
HwMonAttribute::new(HwMonAttributeType::Fan, 1, HwMonAttributeItem::Input),
|
||||
HwMonAttribute::new(HwMonAttributeType::Fan, 1, HwMonAttributeItem::Min),
|
||||
HwMonAttribute::new(HwMonAttributeType::Fan, 1, HwMonAttributeItem::Max)
|
||||
].into_iter()))?.next().expect("Missing capable amdgpu");
|
||||
assert_eq!(hwmon.attribute::<String, _>(HwMonAttribute::name()).expect("name capable but also incapable"), "amdgpu");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,7 @@ use std::path::{Path, PathBuf};
|
|||
use std::convert::AsRef;
|
||||
use std::io::Result as IoResult;
|
||||
|
||||
use crate::SysEntityRawExt;
|
||||
//use crate::SysEntityAttributes;
|
||||
use crate::SysEntityAttributesExt;
|
||||
|
||||
const PSU_DIR_PATH: &'static str = "sys/class/power_supply/";
|
||||
|
||||
|
@ -218,7 +217,7 @@ impl PowerSupplyPath {
|
|||
|
||||
/// Get the power supply type
|
||||
pub fn type_str(&self) -> IoResult<String> {
|
||||
self.attribute_str("type")
|
||||
self.attribute(PowerSupplyAttribute::Type).map_err(|e| e.map_infallible_second())
|
||||
}
|
||||
|
||||
/// Get the power supply type
|
||||
|
@ -267,6 +266,9 @@ impl crate::SysEntityAttributes<PowerSupplyAttribute> for PowerSupplyPath {
|
|||
PowerSupplyAttribute::CapacityAlertMin,
|
||||
PowerSupplyAttribute::CapacityErrorMargin,
|
||||
PowerSupplyAttribute::CapacityLevel,
|
||||
PowerSupplyAttribute::ChargeNow,
|
||||
PowerSupplyAttribute::ChargeFull,
|
||||
PowerSupplyAttribute::ChargeFullDesign,
|
||||
PowerSupplyAttribute::ChargeControlLimit,
|
||||
PowerSupplyAttribute::ChargeControlLimitMax,
|
||||
PowerSupplyAttribute::ChargeControlStartThreshold,
|
||||
|
@ -290,6 +292,8 @@ impl crate::SysEntityAttributes<PowerSupplyAttribute> for PowerSupplyPath {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::SysEntityAttributes;
|
||||
use crate::SysEntityAttributesExt;
|
||||
use crate::SysAttribute;
|
||||
|
||||
#[test]
|
||||
fn power_supply_all() -> std::io::Result<()> {
|
||||
|
@ -308,4 +312,20 @@ mod tests {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn power_supply_capabilities() -> std::io::Result<()> {
|
||||
let sys = crate::SysPath::default();
|
||||
if !PowerSupplyAttribute::Type.exists(&sys.power_supply_by_name("BAT0")) {
|
||||
// skip if system has no battery
|
||||
eprintln!("power_supply test skipped since BAT0 does not exist (maybe running on a desktop PC?)");
|
||||
return Ok(())
|
||||
}
|
||||
let psu = sys.power_supply(crate::capability::attributes([
|
||||
PowerSupplyAttribute::Type,
|
||||
PowerSupplyAttribute::Capacity,
|
||||
].into_iter()))?.next().expect("Missing capable battery");
|
||||
assert!(psu.attribute::<PowerSupplyType, _>(PowerSupplyAttribute::Type).expect("type capable but also incapable") == PowerSupplyType::Battery);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue