Improve .attribute() parse type ergonomics

This commit is contained in:
NGnius (Graham) 2024-07-10 21:00:51 -04:00
parent 942d9f71e5
commit 481844408d
6 changed files with 26 additions and 16 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "sysfuss" name = "sysfuss"
version = "0.3.0" version = "0.4.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"

View file

@ -35,7 +35,7 @@ pub trait SysAttributeExt: SysAttribute {
} }
/// read and parse the attribute value /// read and parse the attribute value
fn parse<T: FromStr<Err=E>, E, ENT: crate::SysEntity + ?Sized>(&self, entity: &ENT) -> Result<T, EitherErr2<std::io::Error, E>> { fn parse<T: FromStr, ENT: crate::SysEntity + ?Sized>(&self, entity: &ENT) -> Result<T, EitherErr2<std::io::Error, <T as FromStr>::Err>> {
let s = self.read_str(entity).map_err(EitherErr2::First)?; let s = self.read_str(entity).map_err(EitherErr2::First)?;
T::from_str(s.trim_end_matches('\n')).map_err(EitherErr2::Second) T::from_str(s.trim_end_matches('\n')).map_err(EitherErr2::Second)
} }

View file

@ -5,6 +5,7 @@ use std::io::Result as IoResult;
const SYS_CLASS_PATH: &str = "sys/class"; const SYS_CLASS_PATH: &str = "sys/class";
/// Generic entity path with basic functionality /// Generic entity path with basic functionality
#[derive(PartialEq, Eq)]
#[cfg_attr(feature = "derive", derive(Debug, Clone))] #[cfg_attr(feature = "derive", derive(Debug, Clone))]
pub struct BasicEntityPath { pub struct BasicEntityPath {
path: PathBuf path: PathBuf
@ -73,6 +74,12 @@ impl crate::SysEntityAttributes<String> for BasicEntityPath {
} }
} }
impl <'a> crate::SysEntityAttributes<&'a str> for BasicEntityPath {
fn capabilities(&self) -> Vec<&'a str> {
panic!("Cannot read capabilities into &str")
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -104,7 +111,7 @@ mod tests {
"temp1_alarm".to_string(), "temp1_alarm".to_string(),
"temp1_crit".to_string(), "temp1_crit".to_string(),
].into_iter()))?.next().expect("Missing any hwmon"); ].into_iter()))?.next().expect("Missing any hwmon");
let attr_value = crate::SysEntityAttributesExt::attribute::<String, _>(&basic, "name".to_owned()).expect("name capable but also incapable"); let attr_value = crate::SysEntityAttributesExt::attribute::<String>(&basic, "name".to_owned()).expect("name capable but also incapable");
println!("Attribute ./name = '{}'", attr_value); println!("Attribute ./name = '{}'", attr_value);
assert!(attr_value == "nvme"); assert!(attr_value == "nvme");
Ok(()) Ok(())
@ -125,7 +132,7 @@ mod tests {
"dev".to_string(), "dev".to_string(),
"uevent".to_string(), "uevent".to_string(),
].into_iter()))?.filter(|basic| basic.name().expect("no name").starts_with("card")).next().expect("Missing drm"); ].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"); let attr_value = crate::SysEntityAttributesExt::attribute::<String>(&basic, "dev".to_owned()).expect("dev capable but also incapable");
println!("Attribute ./dev = '{}'", attr_value); println!("Attribute ./dev = '{}'", attr_value);
assert!(attr_value == expected_dev); assert!(attr_value == expected_dev);
Ok(()) Ok(())

View file

@ -38,7 +38,7 @@ pub trait SysEntity: AsRef<Path> {
/// sysfs class entity functionality extension /// sysfs class entity functionality extension
pub trait SysEntityRawExt: SysEntity { pub trait SysEntityRawExt: SysEntity {
/// Get an attribute on the entity /// Get an attribute on the entity
fn attribute<A: crate::SysAttribute, T: std::str::FromStr<Err=E>, E>(&self, attr: A) -> Result<T, EitherErr2<std::io::Error, E>>; fn attribute<A: crate::SysAttribute, T: std::str::FromStr>(&self, attr: A) -> Result<T, EitherErr2<std::io::Error, <T as std::str::FromStr>::Err>>;
/// Get an attribute by filename in the entity's directory /// Get an attribute by filename in the entity's directory
fn attribute_str<A: AsRef<Path>>(&self, attr: A) -> IoResult<String>; fn attribute_str<A: AsRef<Path>>(&self, attr: A) -> IoResult<String>;
@ -50,7 +50,7 @@ pub trait SysEntityRawExt: SysEntity {
} }
impl <X: SysEntity> SysEntityRawExt for X { impl <X: SysEntity> SysEntityRawExt for X {
fn attribute<A: crate::SysAttribute, T: std::str::FromStr<Err=E>, E>(&self, attr: A) -> Result<T, EitherErr2<std::io::Error, E>> { fn attribute<A: crate::SysAttribute, T: std::str::FromStr>(&self, attr: A) -> Result<T, EitherErr2<std::io::Error, <T as std::str::FromStr>::Err>> {
attr.parse(self) attr.parse(self)
} }
@ -82,7 +82,7 @@ pub trait SysEntityAttributesExt<A: crate::SysAttribute>: SysEntityAttributes<A>
} }
/// Get an attribute on the entity /// Get an attribute on the entity
fn attribute<T: std::str::FromStr<Err=E>, E>(&self, attr: A) -> Result<T, EitherErr2<std::io::Error, E>> { fn attribute<T: std::str::FromStr>(&self, attr: A) -> Result<T, EitherErr2<std::io::Error, <T as std::str::FromStr>::Err>> {
attr.parse(self) attr.parse(self)
} }

View file

@ -61,6 +61,8 @@ pub enum HwMonAttributeType {
Fan, Fan,
/// Current /// Current
Curr, Curr,
/// Frequency
Freq,
} }
impl HwMonAttributeType { impl HwMonAttributeType {
@ -70,6 +72,7 @@ impl HwMonAttributeType {
Self::Temp => "out", Self::Temp => "out",
Self::Fan => "fan", Self::Fan => "fan",
Self::Curr => "curr", Self::Curr => "curr",
Self::Freq => "freq",
} }
} }
@ -204,7 +207,7 @@ 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?;
let value: String = entry.attribute::<String, _>(HwMonAttribute::name()).map_err(|e| match e { let value: String = entry.attribute::<String>(HwMonAttribute::name()).map_err(|e| match e {
crate::EitherErr2::First(e) => e, crate::EitherErr2::First(e) => e,
crate::EitherErr2::Second(_e) => panic!("Infallible"), crate::EitherErr2::Second(_e) => panic!("Infallible"),
})?; })?;
@ -269,8 +272,8 @@ 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::<String, _>(HwMonAttribute::name()).map_err(|e| e.map_infallible_second())? != ""); 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.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()))
} }
@ -287,11 +290,10 @@ mod tests {
} }
let hwmon = sys.hwmon(crate::capability::attributes([ let hwmon = sys.hwmon(crate::capability::attributes([
HwMonAttribute::name(), HwMonAttribute::name(),
HwMonAttribute::new(HwMonAttributeType::Fan, 1, HwMonAttributeItem::Input), HwMonAttribute::new(HwMonAttributeType::In, 0, HwMonAttributeItem::Input),
HwMonAttribute::new(HwMonAttributeType::Fan, 1, HwMonAttributeItem::Min), HwMonAttribute::new(HwMonAttributeType::In, 0, HwMonAttributeItem::Label),
HwMonAttribute::new(HwMonAttributeType::Fan, 1, HwMonAttributeItem::Max)
].into_iter()))?.next().expect("Missing capable amdgpu"); ].into_iter()))?.next().expect("Missing capable amdgpu");
assert_eq!(hwmon.attribute::<String, _>(HwMonAttribute::name()).expect("name capable but also incapable"), "amdgpu"); assert_eq!(hwmon.attribute::<String>(HwMonAttribute::name()).expect("name capable but also incapable"), "amdgpu");
Ok(()) Ok(())
} }
} }

View file

@ -197,6 +197,7 @@ impl std::str::FromStr for PowerSupplyType {
} }
/// power_supply/<entity>/ directory /// power_supply/<entity>/ directory
#[derive(PartialEq, Eq)]
#[cfg_attr(feature = "derive", derive(Debug, Clone))] #[cfg_attr(feature = "derive", derive(Debug, Clone))]
pub struct PowerSupplyPath { pub struct PowerSupplyPath {
inner: crate::BasicEntityPath, inner: crate::BasicEntityPath,
@ -293,7 +294,7 @@ mod tests {
use super::*; use super::*;
use crate::SysEntityAttributes; use crate::SysEntityAttributes;
use crate::SysEntityAttributesExt; use crate::SysEntityAttributesExt;
use crate::SysAttribute; use crate::SysAttributeExt;
#[test] #[test]
fn power_supply_all() -> std::io::Result<()> { fn power_supply_all() -> std::io::Result<()> {
@ -325,7 +326,7 @@ mod tests {
PowerSupplyAttribute::Type, PowerSupplyAttribute::Type,
PowerSupplyAttribute::Capacity, PowerSupplyAttribute::Capacity,
].into_iter()))?.next().expect("Missing capable battery"); ].into_iter()))?.next().expect("Missing capable battery");
assert!(psu.attribute::<PowerSupplyType, _>(PowerSupplyAttribute::Type).expect("type capable but also incapable") == PowerSupplyType::Battery); assert!(psu.attribute::<PowerSupplyType>(PowerSupplyAttribute::Type).expect("type capable but also incapable") == PowerSupplyType::Battery);
Ok(()) Ok(())
} }
} }