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]
name = "sysfuss"
version = "0.3.0"
version = "0.4.0"
edition = "2021"
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
description = "sysfs wrapper for convenience"

View file

@ -35,7 +35,7 @@ pub trait SysAttributeExt: SysAttribute {
}
/// 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)?;
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";
/// Generic entity path with basic functionality
#[derive(PartialEq, Eq)]
#[cfg_attr(feature = "derive", derive(Debug, Clone))]
pub struct BasicEntityPath {
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)]
mod tests {
use super::*;
@ -104,7 +111,7 @@ mod tests {
"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");
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(())
@ -125,7 +132,7 @@ mod tests {
"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");
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(())

View file

@ -38,7 +38,7 @@ pub trait SysEntity: AsRef<Path> {
/// sysfs class entity functionality extension
pub trait SysEntityRawExt: SysEntity {
/// 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
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 {
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)
}
@ -82,7 +82,7 @@ pub trait SysEntityAttributesExt<A: crate::SysAttribute>: SysEntityAttributes<A>
}
/// 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)
}

View file

@ -61,6 +61,8 @@ pub enum HwMonAttributeType {
Fan,
/// Current
Curr,
/// Frequency
Freq,
}
impl HwMonAttributeType {
@ -70,6 +72,7 @@ impl HwMonAttributeType {
Self::Temp => "out",
Self::Fan => "fan",
Self::Curr => "curr",
Self::Freq => "freq",
}
}
@ -204,7 +207,7 @@ impl HwMonPath {
pub(crate) fn name(root: &crate::SysPath, name: &str) -> IoResult<Option<Self>> {
for entry in Self::all(root)? {
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::Second(_e) => panic!("Infallible"),
})?;
@ -269,8 +272,8 @@ mod tests {
assert!(!all_hwmon.is_empty());
for hwmon in all_hwmon.into_iter() {
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())?.ends_with("\n"));
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()))
}
@ -287,11 +290,10 @@ mod tests {
}
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)
HwMonAttribute::new(HwMonAttributeType::In, 0, HwMonAttributeItem::Input),
HwMonAttribute::new(HwMonAttributeType::In, 0, HwMonAttributeItem::Label),
].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(())
}
}

View file

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