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]
|
[package]
|
||||||
name = "sysfuss"
|
name = "sysfuss"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
|
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
|
||||||
description = "sysfs wrapper for convenience"
|
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>>> {
|
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);
|
let dir_path = root.as_ref().join(SYS_CLASS_PATH).join(class);
|
||||||
dir_path.read_dir()
|
Ok(dir_path.read_dir()?
|
||||||
.map(
|
.filter_map(
|
||||||
|iter| iter.filter_map(
|
|
||||||
|entry| entry.map(
|
|entry| entry.map(
|
||||||
|entry| if entry.path().is_file() { None } else { Some(Self::new(entry.path())) }
|
|entry| if entry.path().is_file() { None } else { Some(Self::new(entry.path())) }
|
||||||
).transpose()))
|
).transpose()))
|
||||||
|
@ -77,6 +76,7 @@ impl crate::SysEntityAttributes<String> for BasicEntityPath {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::SysEntity;
|
||||||
use crate::SysEntityRawExt;
|
use crate::SysEntityRawExt;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -89,4 +89,45 @@ mod tests {
|
||||||
}
|
}
|
||||||
Ok(())
|
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> {
|
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 {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
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/";
|
const HWMON_DIR_PATH: &'static str = "sys/class/hwmon/";
|
||||||
|
|
||||||
|
use crate::SysEntityAttributesExt;
|
||||||
|
|
||||||
/// Attribute representation.
|
/// Attribute representation.
|
||||||
/// Attribute files are usually in the format `<type><number>_<item>`.
|
/// Attribute files are usually in the format `<type><number>_<item>`.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
@ -202,7 +204,11 @@ impl HwMonPath {
|
||||||
pub(crate) fn name(root: &crate::SysPath, name: &str) -> IoResult<Option<Self>> {
|
pub(crate) fn name(root: &crate::SysPath, name: &str) -> IoResult<Option<Self>> {
|
||||||
for entry in Self::all(root)? {
|
for entry in Self::all(root)? {
|
||||||
let entry = entry?;
|
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))
|
return Ok(Some(entry))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,11 +221,6 @@ impl HwMonPath {
|
||||||
std::fs::read_to_string(self.path.join(name))
|
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.
|
/// Get the path to a hwmon attribute.
|
||||||
/// Use `HwMonPath.as_ref().join(attribute_str)` for non-standard attributes
|
/// Use `HwMonPath.as_ref().join(attribute_str)` for non-standard attributes
|
||||||
pub fn path_to(&self, attr: HwMonAttribute) -> PathBuf {
|
pub fn path_to(&self, attr: HwMonAttribute) -> PathBuf {
|
||||||
|
@ -239,7 +240,7 @@ impl crate::SysEntity for HwMonPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> IoResult<String> {
|
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());
|
assert!(!all_hwmon.is_empty());
|
||||||
for hwmon in all_hwmon.into_iter() {
|
for hwmon in all_hwmon.into_iter() {
|
||||||
let hwmon = hwmon?;
|
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().is_empty());
|
||||||
assert!(hwmon.capabilities().contains(&HwMonAttribute::name()))
|
assert!(hwmon.capabilities().contains(&HwMonAttribute::name()))
|
||||||
}
|
}
|
||||||
Ok(())
|
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::convert::AsRef;
|
||||||
use std::io::Result as IoResult;
|
use std::io::Result as IoResult;
|
||||||
|
|
||||||
use crate::SysEntityRawExt;
|
use crate::SysEntityAttributesExt;
|
||||||
//use crate::SysEntityAttributes;
|
|
||||||
|
|
||||||
const PSU_DIR_PATH: &'static str = "sys/class/power_supply/";
|
const PSU_DIR_PATH: &'static str = "sys/class/power_supply/";
|
||||||
|
|
||||||
|
@ -218,7 +217,7 @@ impl PowerSupplyPath {
|
||||||
|
|
||||||
/// Get the power supply type
|
/// Get the power supply type
|
||||||
pub fn type_str(&self) -> IoResult<String> {
|
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
|
/// Get the power supply type
|
||||||
|
@ -267,6 +266,9 @@ impl crate::SysEntityAttributes<PowerSupplyAttribute> for PowerSupplyPath {
|
||||||
PowerSupplyAttribute::CapacityAlertMin,
|
PowerSupplyAttribute::CapacityAlertMin,
|
||||||
PowerSupplyAttribute::CapacityErrorMargin,
|
PowerSupplyAttribute::CapacityErrorMargin,
|
||||||
PowerSupplyAttribute::CapacityLevel,
|
PowerSupplyAttribute::CapacityLevel,
|
||||||
|
PowerSupplyAttribute::ChargeNow,
|
||||||
|
PowerSupplyAttribute::ChargeFull,
|
||||||
|
PowerSupplyAttribute::ChargeFullDesign,
|
||||||
PowerSupplyAttribute::ChargeControlLimit,
|
PowerSupplyAttribute::ChargeControlLimit,
|
||||||
PowerSupplyAttribute::ChargeControlLimitMax,
|
PowerSupplyAttribute::ChargeControlLimitMax,
|
||||||
PowerSupplyAttribute::ChargeControlStartThreshold,
|
PowerSupplyAttribute::ChargeControlStartThreshold,
|
||||||
|
@ -290,6 +292,8 @@ impl crate::SysEntityAttributes<PowerSupplyAttribute> for PowerSupplyPath {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::SysEntityAttributes;
|
use crate::SysEntityAttributes;
|
||||||
|
use crate::SysEntityAttributesExt;
|
||||||
|
use crate::SysAttribute;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn power_supply_all() -> std::io::Result<()> {
|
fn power_supply_all() -> std::io::Result<()> {
|
||||||
|
@ -308,4 +312,20 @@ mod tests {
|
||||||
}
|
}
|
||||||
Ok(())
|
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