Make capability filtering more powerful, fix not working with custom attributes

This commit is contained in:
NGnius (Graham) 2023-09-03 11:25:34 -04:00
parent fe1f031c33
commit 942d9f71e5
9 changed files with 149 additions and 145 deletions

View file

@ -1,6 +1,6 @@
[package]
name = "sysfuss"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
description = "sysfs wrapper for convenience"

View file

@ -5,35 +5,9 @@ use std::str::FromStr;
use crate::EitherErr2;
/// sysfs entity attribute file functionality
pub trait SysAttribute {
pub trait SysAttribute: Eq {
/// attribute file name
fn filename(&self) -> PathBuf;
/// full path to attribute file
fn path(&self, entity: &dyn crate::SysEntity) -> PathBuf {
entity.as_ref().join(self.filename())
}
/// returns true if the path to the attribute file exists
fn exists(&self, entity: &dyn crate::SysEntity) -> bool {
let path = self.path(entity);
path.exists() && path.is_file()
}
/// read attribute value from sysfs
fn read_value(&self, entity: &dyn crate::SysEntity) -> IoResult<Vec<u8>> {
std::fs::read(self.path(entity))
}
/// write attribute value to sysfs
fn write_value(&self, entity: &dyn crate::SysEntity, value: &[u8]) -> IoResult<()> {
std::fs::write(self.path(entity), value)
}
/// Returns true if the attribute is readonly
fn readonly(&self, entity: &dyn crate::SysEntity) -> bool {
self.path(entity).metadata().map(|meta| meta.permissions().readonly()).unwrap_or(false)
}
}
impl SysAttribute for String {
@ -49,22 +23,48 @@ impl SysAttribute for &str {
}
/// sysfs entity attribute functionality extension
pub trait SysAttributeExt: SysAttribute + Eq {
pub trait SysAttributeExt: SysAttribute {
/// read attribute as string
fn read_str(&self, entity: &dyn crate::SysEntity) -> IoResult<String> {
fn read_str<E: crate::SysEntity + ?Sized>(&self, entity: &E) -> IoResult<String> {
std::fs::read_to_string(self.path(entity))
}
/// write attribute string value to sysfsfilter_map
fn write_str(&self, entity: &dyn crate::SysEntity, s: &str) -> IoResult<()> {
fn write_str<E: crate::SysEntity + ?Sized>(&self, entity: &E, s: &str) -> IoResult<()> {
std::fs::write(self.path(entity), s.as_bytes())
}
/// read and parse the attribute value
fn parse<T: FromStr<Err=E>, E>(&self, entity: &dyn crate::SysEntity) -> Result<T, EitherErr2<std::io::Error, E>> {
fn parse<T: FromStr<Err=E>, E, ENT: crate::SysEntity + ?Sized>(&self, entity: &ENT) -> Result<T, EitherErr2<std::io::Error, E>> {
let s = self.read_str(entity).map_err(EitherErr2::First)?;
T::from_str(s.trim_end_matches('\n')).map_err(EitherErr2::Second)
}
/// full path to attribute file
fn path<E: crate::SysEntity + ?Sized>(&self, entity: &E) -> PathBuf {
entity.as_ref().join(self.filename())
}
/// returns true if the path to the attribute file exists
fn exists<E: crate::SysEntity + ?Sized>(&self, entity: &E) -> bool {
let path = self.path(entity);
path.exists() && path.is_file()
}
/// read attribute value from sysfs
fn read_value<E: crate::SysEntity + ?Sized>(&self, entity: &E) -> IoResult<Vec<u8>> {
std::fs::read(self.path(entity))
}
/// write attribute value to sysfs
fn write_value<E: crate::SysEntity + ?Sized>(&self, entity: &E, value: &[u8]) -> IoResult<()> {
std::fs::write(self.path(entity), value)
}
/// Returns true if the attribute is readonly
fn readonly<E: crate::SysEntity + ?Sized>(&self, entity: &E) -> bool {
self.path(entity).metadata().map(|meta| meta.permissions().readonly()).unwrap_or(false)
}
}
impl <X: SysAttribute + Eq> SysAttributeExt for X {}

View file

@ -1,88 +0,0 @@
//! sysfs class entity capability filtering
/// Capabilities filter
pub trait Capabilities<A: crate::SysAttribute, R: std::borrow::Borrow<A>> {
/// Are the required capabilities (self) satisfied by the provided capabilities (the parameter)?
fn can(&mut self, capabilities: &Vec<R>) -> bool;
}
impl <F: FnMut(&Vec<R>) -> bool, A: crate::SysAttribute, R: std::borrow::Borrow<A>> Capabilities<A, R> for F {
fn can(&mut self, capabilities: &Vec<R>) -> bool {
(self)(capabilities)
}
}
/// Build a Capabilities implementor which needs specific attributes to exist
pub fn attributes<'a, A: crate::SysAttribute + Eq + 'a, R: std::borrow::Borrow<A> + 'a>(attributes: impl Iterator<Item=R>) -> impl Capabilities<A, R> + 'a {
AllNeedsCapabilities::new(
attributes.map(|attr: R| { move |other_attr: &R| attr.borrow() == other_attr.borrow() }).collect()
)
}
/// Build a Capabilities implementor which needs specific functions to evaluate to true for some attribute
pub fn complex<'a, A: crate::SysAttribute + 'a, R: std::borrow::Borrow<A> + 'a, F: (FnMut(&R) -> bool) + 'a>(requirements: impl Iterator<Item=F>) -> impl Capabilities<A, R> + 'a {
AllNeedsCapabilities::new(
requirements.collect()
)
}
struct AllNeedsCapabilities<'a, 'f, A: crate::SysAttribute + 'a, R: std::borrow::Borrow<A> + 'a, F: (FnMut(&R) -> bool) + 'f> {
wanted: Vec<F>,
_attr_ty: std::marker::PhantomData<A>,
_attr_brw_ty: std::marker::PhantomData<R>,
_life_attr_ty: std::marker::PhantomData<&'a ()>,
_life_fn_ty: std::marker::PhantomData<&'f ()>,
}
impl <'a, 'f, A: crate::SysAttribute + 'a, R: std::borrow::Borrow<A> + 'a, F: (FnMut(&R) -> bool) + 'f> AllNeedsCapabilities<'a, 'f, A, R, F> {
pub(crate) fn new(wanted: Vec<F>) -> Self {
Self {
wanted,
_attr_ty: Default::default(),
_attr_brw_ty: Default::default(),
_life_attr_ty: Default::default(),
_life_fn_ty: Default::default(),
}
}
}
impl <'a, 'f, A: crate::SysAttribute + 'a, R: std::borrow::Borrow<A> + 'a, F: (FnMut(&R) -> bool) + 'f> Capabilities<A, R> for AllNeedsCapabilities<'a, 'f, A, R, F> {
fn can(&mut self, capabilities: &Vec<R>) -> bool {
let mut wants = vec![false; self.wanted.len()];
for capability in capabilities {
if let Some(pos) = self.wanted.iter_mut().position(|w| w(capability)) {
wants[pos] = true;
}
}
wants.into_iter().all(|item| item)
}
}
/// Chain two capabilities requirements together such that the new capabilities are only satisfied when both inner capabilities are satisfied
pub fn and<A: crate::SysAttribute, R: std::borrow::Borrow<A>>(first: impl Capabilities<A, R>, second: impl Capabilities<A, R>) -> impl Capabilities<A, R> {
ChainCapabilities::new(first, second)
}
struct ChainCapabilities<A: crate::SysAttribute, R: std::borrow::Borrow<A>, X: Capabilities<A, R>, Y: Capabilities<A, R>> {
first: X,
second: Y,
_attr_ty: std::marker::PhantomData<A>,
_attr_brw_ty: std::marker::PhantomData<R>,
}
impl <A: crate::SysAttribute, R: std::borrow::Borrow<A>, X: Capabilities<A, R>, Y: Capabilities<A, R>> ChainCapabilities<A, R, X, Y> {
pub(crate) fn new(first: X, second: Y) -> Self {
Self {
first,
second,
_attr_ty: Default::default(),
_attr_brw_ty: Default::default(),
}
}
}
impl <A: crate::SysAttribute, R: std::borrow::Borrow<A>, X: Capabilities<A, R>, Y: Capabilities<A, R>> Capabilities<A, R> for ChainCapabilities<A, R, X, Y> {
fn can(&mut self, capabilities: &Vec<R>) -> bool {
self.first.can(capabilities) && self.second.can(capabilities)
}
}

11
src/capability/all.rs Normal file
View file

@ -0,0 +1,11 @@
pub struct AllCapabilities<A: crate::SysAttribute, E: crate::SysEntityAttributes<A> + ?Sized, F: FnMut(&E) -> bool> {
pub(crate) required: Vec<F>,
pub(crate) _attr_ty: std::marker::PhantomData<A>,
pub(crate) _entity_ty: std::marker::PhantomData<E>,
}
impl <A: crate::SysAttribute, E: crate::SysEntityAttributes<A> + ?Sized, F: FnMut(&E) -> bool> super::Capabilities<A, E> for AllCapabilities<A, E, F> {
fn satisfies(&mut self, entity: &E) -> bool {
self.required.iter_mut().any(|f| (f)(entity))
}
}

11
src/capability/any.rs Normal file
View file

@ -0,0 +1,11 @@
pub struct AnyCapabilities<A: crate::SysAttribute, E: crate::SysEntityAttributes<A> + ?Sized, F: FnMut(&E) -> bool> {
pub(crate) required: Vec<F>,
pub(crate) _attr_ty: std::marker::PhantomData<A>,
pub(crate) _entity_ty: std::marker::PhantomData<E>,
}
impl <A: crate::SysAttribute, E: crate::SysEntityAttributes<A> + ?Sized, F: FnMut(&E) -> bool> super::Capabilities<A, E> for AnyCapabilities<A, E, F> {
fn satisfies(&mut self, entity: &E) -> bool {
self.required.iter_mut().all(|f| (f)(entity))
}
}

View file

@ -0,0 +1,13 @@
use crate::SysAttributeExt;
pub struct AttributeCapabilities<A: crate::SysAttribute, E: crate::SysEntityAttributes<A> + ?Sized> {
pub(crate) required: Vec<A>,
pub(crate) _entity_ty: std::marker::PhantomData<E>,
}
impl <A: crate::SysAttribute, E: crate::SysEntityAttributes<A> + ?Sized> super::Capabilities<A, E> for AttributeCapabilities<A, E> {
fn satisfies(&mut self, entity: &E) -> bool {
self.required.iter()
.all(|attr| attr.path(entity).is_file())
}
}

53
src/capability/mod.rs Normal file
View file

@ -0,0 +1,53 @@
//! sysfs class entity capability filtering
mod all;
mod any;
mod attributes;
use all::AllCapabilities;
use any::AnyCapabilities;
use attributes::AttributeCapabilities;
/// Capabilities filter
pub trait Capabilities<A: crate::SysAttribute, E: crate::SysEntityAttributes<A> + ?Sized> {
/// Are the required capabilities (self) satisfied by the provided capabilities (the parameter)?
fn satisfies(&mut self, entity: &E) -> bool;
}
impl <F: FnMut(&E) -> bool, A: crate::SysAttribute, E: crate::SysEntityAttributes<A> + ?Sized> Capabilities<A, E> for F {
fn satisfies(&mut self, entity: &E) -> bool {
(self)(entity)
}
}
/// Build a Capabilities implementor which needs specific attributes to exist
pub fn attributes<A: crate::SysAttribute, E: crate::SysEntityAttributes<A> + ?Sized>(iter: impl Iterator<Item=A>) -> impl Capabilities<A, E> {
AttributeCapabilities {
required: iter.collect(),
_entity_ty: Default::default(),
}
}
/// Chain two capabilities requirements together such that the new capabilities are only satisfied when both inner capabilities are satisfied
pub fn and<'a, A: crate::SysAttribute + 'a, E: crate::SysEntityAttributes<A> + ?Sized + 'a>(mut first: impl Capabilities<A, E> + 'a, mut second: impl Capabilities<A, E> + 'a) -> impl Capabilities<A, E> + 'a {
AllCapabilities {
required: vec![
Box::new(move |ent: &E| first.satisfies(ent)) as Box<dyn FnMut(&E) -> bool>,
Box::new(move |ent: &E| second.satisfies(ent)) as Box<dyn FnMut(&E) -> bool>,
],
_attr_ty: Default::default(),
_entity_ty: Default::default(),
}
}
/// Chain two capabilities requirements together such that the new capabilities are satisfied when either of the inner capabilities is satisfied
pub fn or<'a, A: crate::SysAttribute + 'a, E: crate::SysEntityAttributes<A> + ?Sized + 'a>(mut first: impl Capabilities<A, E> + 'a, mut second: impl Capabilities<A, E> + 'a) -> impl Capabilities<A, E> + 'a {
AnyCapabilities {
required: vec![
Box::new(move |ent: &E| first.satisfies(ent)) as Box<dyn FnMut(&E) -> bool>,
Box::new(move |ent: &E| second.satisfies(ent)) as Box<dyn FnMut(&E) -> bool>,
],
_attr_ty: Default::default(),
_entity_ty: Default::default(),
}
}

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 + Eq, 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<Err=E>, E>(&self, attr: A) -> Result<T, EitherErr2<std::io::Error, E>>;
/// 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 + Eq, 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<Err=E>, E>(&self, attr: A) -> Result<T, EitherErr2<std::io::Error, E>> {
attr.parse(self)
}
@ -69,26 +69,16 @@ impl <X: SysEntity> SysEntityRawExt for X {
/// sysfs class entity attribute type indicator
pub trait SysEntityAttributes<A: crate::SysAttribute + Eq>: SysEntity + Sized {
pub trait SysEntityAttributes<A: crate::SysAttribute>: SysEntity {
/// Get attributes available on this entity;
fn capabilities(&self) -> Vec<A>;
/// Read entity attribute value
fn read_value(&self, attr: A) -> IoResult<Vec<u8>> {
attr.read_value(self)
}
/// Write entity attribute value
fn write_value(&self, attr: A, value: &[u8]) -> IoResult<()> {
attr.write_value(self, value)
}
}
/// sysfs class entity attribute type extension
pub trait SysEntityAttributesExt<A: crate::SysAttribute + Eq>: SysEntityAttributes<A> {
pub trait SysEntityAttributesExt<A: crate::SysAttribute>: SysEntityAttributes<A> + Sized {
/// Returns true if self is capable of the provided capabilities
fn capable<C: crate::capability::Capabilities<A, A>>(&self, mut capabilities: C) -> bool {
capabilities.can(&self.capabilities())
fn capable<C: crate::capability::Capabilities<A, Self>>(&self, mut capabilities: C) -> bool {
capabilities.satisfies(self)
}
/// Get an attribute on the entity
@ -100,9 +90,24 @@ pub trait SysEntityAttributesExt<A: crate::SysAttribute + Eq>: SysEntityAttribut
fn set<V: std::string::ToString>(&self, attr: A, value: V) -> IoResult<()> {
attr.write_str(self, &value.to_string())
}
/// Read entity attribute value
fn read_value(&self, attr: &A) -> IoResult<Vec<u8>> {
attr.read_value(self)
}
impl <A: crate::SysAttribute + Eq, X: SysEntityAttributes<A>> SysEntityAttributesExt<A> for X {}
/// Write entity attribute value
fn write_value(&self, attr: &A, value: &[u8]) -> IoResult<()> {
attr.write_value(self, value)
}
/// Returns true when the entity has the attribute
fn exists(&self, attr: &A) -> bool {
attr.exists(self)
}
}
impl <A: crate::SysAttribute, X: SysEntityAttributes<A>> SysEntityAttributesExt<A> for X {}
/// sysfs class entity implementors
#[cfg_attr(feature = "derive", derive(Debug, Clone))]

View file

@ -1,7 +1,7 @@
use std::path::{Path, PathBuf};
use std::convert::AsRef;
use crate::{SysEntity, SysEntityAttributes};
use crate::SysEntity;
/// sysfs root
#[cfg_attr(feature = "derive", derive(Debug))]
@ -43,10 +43,10 @@ impl SysPath {
}
/// Find a hardware monitor by capabilities
pub fn hwmon(&self, mut c: impl crate::capability::Capabilities<crate::HwMonAttribute, crate::HwMonAttribute>) -> std::io::Result<impl Iterator<Item=crate::HwMonPath>> {
pub fn hwmon(&self, mut c: impl crate::capability::Capabilities<crate::HwMonAttribute, crate::HwMonPath>) -> std::io::Result<impl Iterator<Item=crate::HwMonPath>> {
Ok(crate::HwMonPath::all(self.root.clone())?
.filter_map(|p| p.ok())
.filter(move |p| c.can(&p.capabilities())))
.filter(move |p| c.satisfies(p)))
}
/// Find a power supply entry by name
@ -55,18 +55,17 @@ impl SysPath {
}
/// Find a power supply by capabilities
pub fn power_supply(&self, mut c: impl crate::capability::Capabilities<crate::PowerSupplyAttribute, crate::PowerSupplyAttribute>) -> std::io::Result<impl Iterator<Item=crate::PowerSupplyPath>> {
pub fn power_supply(&self, mut c: impl crate::capability::Capabilities<crate::PowerSupplyAttribute, crate::PowerSupplyPath>) -> std::io::Result<impl Iterator<Item=crate::PowerSupplyPath>> {
Ok(crate::PowerSupplyPath::all(self.root.clone())?
.filter_map(|p| p.ok())
.filter(move |p| c.can(&p.capabilities())))
.filter(move |p| c.satisfies(p)))
}
/// Find entities in a sysfs class by capabilities
pub fn class(&self, class: impl AsRef<Path>, mut c: impl crate::capability::Capabilities<String, String>) -> std::io::Result<impl Iterator<Item=crate::BasicEntityPath>> {
pub fn class(&self, class: impl AsRef<Path>, mut c: impl crate::capability::Capabilities<String, crate::BasicEntityPath>) -> std::io::Result<impl Iterator<Item=crate::BasicEntityPath>> {
Ok(crate::BasicEntityPath::all(self.root.clone(), class)?
.filter_map(|p| p.ok())
.filter(move |p| c.can(&p.capabilities()))
)
.filter(move |p| c.satisfies(p)))
}
pub(crate) fn from_entity_path(entity: impl AsRef<Path>) -> Option<Self> {