From 942d9f71e5f886d937f4a6a5fd36bfb1d689ccd3 Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Sun, 3 Sep 2023 11:25:34 -0400 Subject: [PATCH] Make capability filtering more powerful, fix not working with custom attributes --- Cargo.toml | 2 +- src/attribute.rs | 62 ++++++++++++------------- src/capability.rs | 88 ------------------------------------ src/capability/all.rs | 11 +++++ src/capability/any.rs | 11 +++++ src/capability/attributes.rs | 13 ++++++ src/capability/mod.rs | 53 ++++++++++++++++++++++ src/entity.rs | 39 +++++++++------- src/syspath.rs | 15 +++--- 9 files changed, 149 insertions(+), 145 deletions(-) delete mode 100644 src/capability.rs create mode 100644 src/capability/all.rs create mode 100644 src/capability/any.rs create mode 100644 src/capability/attributes.rs create mode 100644 src/capability/mod.rs diff --git a/Cargo.toml b/Cargo.toml index d45bdee..4a65e5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sysfuss" -version = "0.2.0" +version = "0.3.0" edition = "2021" authors = ["NGnius (Graham) "] description = "sysfs wrapper for convenience" diff --git a/src/attribute.rs b/src/attribute.rs index a3a3dcb..c89599b 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -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> { - 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 { + fn read_str(&self, entity: &E) -> IoResult { 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(&self, entity: &E, s: &str) -> IoResult<()> { std::fs::write(self.path(entity), s.as_bytes()) } /// read and parse the attribute value - fn parse, E>(&self, entity: &dyn crate::SysEntity) -> Result> { + fn parse, E, ENT: crate::SysEntity + ?Sized>(&self, entity: &ENT) -> Result> { 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(&self, entity: &E) -> PathBuf { + entity.as_ref().join(self.filename()) + } + + /// returns true if the path to the attribute file exists + fn exists(&self, entity: &E) -> bool { + let path = self.path(entity); + path.exists() && path.is_file() + } + + /// read attribute value from sysfs + fn read_value(&self, entity: &E) -> IoResult> { + std::fs::read(self.path(entity)) + } + + /// write attribute value to sysfs + fn write_value(&self, entity: &E, value: &[u8]) -> IoResult<()> { + std::fs::write(self.path(entity), value) + } + + /// Returns true if the attribute is readonly + fn readonly(&self, entity: &E) -> bool { + self.path(entity).metadata().map(|meta| meta.permissions().readonly()).unwrap_or(false) + } } impl SysAttributeExt for X {} diff --git a/src/capability.rs b/src/capability.rs deleted file mode 100644 index 1363b24..0000000 --- a/src/capability.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! sysfs class entity capability filtering - -/// Capabilities filter -pub trait Capabilities> { - /// Are the required capabilities (self) satisfied by the provided capabilities (the parameter)? - fn can(&mut self, capabilities: &Vec) -> bool; -} - -impl ) -> bool, A: crate::SysAttribute, R: std::borrow::Borrow> Capabilities for F { - fn can(&mut self, capabilities: &Vec) -> 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>(attributes: impl Iterator) -> impl Capabilities + '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, F: (FnMut(&R) -> bool) + 'a>(requirements: impl Iterator) -> impl Capabilities + 'a { - AllNeedsCapabilities::new( - requirements.collect() - ) -} - -struct AllNeedsCapabilities<'a, 'f, A: crate::SysAttribute + 'a, R: std::borrow::Borrow + 'a, F: (FnMut(&R) -> bool) + 'f> { - wanted: Vec, - _attr_ty: std::marker::PhantomData, - _attr_brw_ty: std::marker::PhantomData, - _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, F: (FnMut(&R) -> bool) + 'f> AllNeedsCapabilities<'a, 'f, A, R, F> { - pub(crate) fn new(wanted: Vec) -> 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, F: (FnMut(&R) -> bool) + 'f> Capabilities for AllNeedsCapabilities<'a, 'f, A, R, F> { - fn can(&mut self, capabilities: &Vec) -> 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>(first: impl Capabilities, second: impl Capabilities) -> impl Capabilities { - ChainCapabilities::new(first, second) -} - -struct ChainCapabilities, X: Capabilities, Y: Capabilities> { - first: X, - second: Y, - _attr_ty: std::marker::PhantomData, - _attr_brw_ty: std::marker::PhantomData, -} - -impl , X: Capabilities, Y: Capabilities> ChainCapabilities { - pub(crate) fn new(first: X, second: Y) -> Self { - Self { - first, - second, - _attr_ty: Default::default(), - _attr_brw_ty: Default::default(), - } - } -} - -impl , X: Capabilities, Y: Capabilities> Capabilities for ChainCapabilities { - fn can(&mut self, capabilities: &Vec) -> bool { - self.first.can(capabilities) && self.second.can(capabilities) - } -} diff --git a/src/capability/all.rs b/src/capability/all.rs new file mode 100644 index 0000000..80f53d8 --- /dev/null +++ b/src/capability/all.rs @@ -0,0 +1,11 @@ +pub struct AllCapabilities + ?Sized, F: FnMut(&E) -> bool> { + pub(crate) required: Vec, + pub(crate) _attr_ty: std::marker::PhantomData, + pub(crate) _entity_ty: std::marker::PhantomData, +} + +impl + ?Sized, F: FnMut(&E) -> bool> super::Capabilities for AllCapabilities { + fn satisfies(&mut self, entity: &E) -> bool { + self.required.iter_mut().any(|f| (f)(entity)) + } +} diff --git a/src/capability/any.rs b/src/capability/any.rs new file mode 100644 index 0000000..7736380 --- /dev/null +++ b/src/capability/any.rs @@ -0,0 +1,11 @@ +pub struct AnyCapabilities + ?Sized, F: FnMut(&E) -> bool> { + pub(crate) required: Vec, + pub(crate) _attr_ty: std::marker::PhantomData, + pub(crate) _entity_ty: std::marker::PhantomData, +} + +impl + ?Sized, F: FnMut(&E) -> bool> super::Capabilities for AnyCapabilities { + fn satisfies(&mut self, entity: &E) -> bool { + self.required.iter_mut().all(|f| (f)(entity)) + } +} diff --git a/src/capability/attributes.rs b/src/capability/attributes.rs new file mode 100644 index 0000000..73933c0 --- /dev/null +++ b/src/capability/attributes.rs @@ -0,0 +1,13 @@ +use crate::SysAttributeExt; + +pub struct AttributeCapabilities + ?Sized> { + pub(crate) required: Vec, + pub(crate) _entity_ty: std::marker::PhantomData, +} + +impl + ?Sized> super::Capabilities for AttributeCapabilities { + fn satisfies(&mut self, entity: &E) -> bool { + self.required.iter() + .all(|attr| attr.path(entity).is_file()) + } +} diff --git a/src/capability/mod.rs b/src/capability/mod.rs new file mode 100644 index 0000000..d31063e --- /dev/null +++ b/src/capability/mod.rs @@ -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 + ?Sized> { + /// Are the required capabilities (self) satisfied by the provided capabilities (the parameter)? + fn satisfies(&mut self, entity: &E) -> bool; +} + +impl bool, A: crate::SysAttribute, E: crate::SysEntityAttributes + ?Sized> Capabilities for F { + fn satisfies(&mut self, entity: &E) -> bool { + (self)(entity) + } +} + +/// Build a Capabilities implementor which needs specific attributes to exist +pub fn attributes + ?Sized>(iter: impl Iterator) -> impl Capabilities { + 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 + ?Sized + 'a>(mut first: impl Capabilities + 'a, mut second: impl Capabilities + 'a) -> impl Capabilities + 'a { + AllCapabilities { + required: vec![ + Box::new(move |ent: &E| first.satisfies(ent)) as Box bool>, + Box::new(move |ent: &E| second.satisfies(ent)) as Box 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 + ?Sized + 'a>(mut first: impl Capabilities + 'a, mut second: impl Capabilities + 'a) -> impl Capabilities + 'a { + AnyCapabilities { + required: vec![ + Box::new(move |ent: &E| first.satisfies(ent)) as Box bool>, + Box::new(move |ent: &E| second.satisfies(ent)) as Box bool>, + ], + _attr_ty: Default::default(), + _entity_ty: Default::default(), + } +} diff --git a/src/entity.rs b/src/entity.rs index 59cb380..5cfee83 100644 --- a/src/entity.rs +++ b/src/entity.rs @@ -38,7 +38,7 @@ pub trait SysEntity: AsRef { /// sysfs class entity functionality extension pub trait SysEntityRawExt: SysEntity { /// Get an attribute on the entity - fn attribute, E>(&self, attr: A) -> Result>; + fn attribute, E>(&self, attr: A) -> Result>; /// Get an attribute by filename in the entity's directory fn attribute_str>(&self, attr: A) -> IoResult; @@ -50,7 +50,7 @@ pub trait SysEntityRawExt: SysEntity { } impl SysEntityRawExt for X { - fn attribute, E>(&self, attr: A) -> Result> { + fn attribute, E>(&self, attr: A) -> Result> { attr.parse(self) } @@ -69,26 +69,16 @@ impl SysEntityRawExt for X { /// sysfs class entity attribute type indicator -pub trait SysEntityAttributes: SysEntity + Sized { +pub trait SysEntityAttributes: SysEntity { /// Get attributes available on this entity; fn capabilities(&self) -> Vec; - - /// Read entity attribute value - fn read_value(&self, attr: A) -> IoResult> { - 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: SysEntityAttributes { +pub trait SysEntityAttributesExt: SysEntityAttributes + Sized { /// Returns true if self is capable of the provided capabilities - fn capable>(&self, mut capabilities: C) -> bool { - capabilities.can(&self.capabilities()) + fn capable>(&self, mut capabilities: C) -> bool { + capabilities.satisfies(self) } /// Get an attribute on the entity @@ -100,9 +90,24 @@ pub trait SysEntityAttributesExt: SysEntityAttribut fn set(&self, attr: A, value: V) -> IoResult<()> { attr.write_str(self, &value.to_string()) } + + /// Read entity attribute value + fn read_value(&self, attr: &A) -> IoResult> { + attr.read_value(self) + } + + /// 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 > SysEntityAttributesExt for X {} +impl > SysEntityAttributesExt for X {} /// sysfs class entity implementors #[cfg_attr(feature = "derive", derive(Debug, Clone))] diff --git a/src/syspath.rs b/src/syspath.rs index b069dae..a671222 100644 --- a/src/syspath.rs +++ b/src/syspath.rs @@ -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) -> std::io::Result> { + pub fn hwmon(&self, mut c: impl crate::capability::Capabilities) -> std::io::Result> { 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) -> std::io::Result> { + pub fn power_supply(&self, mut c: impl crate::capability::Capabilities) -> std::io::Result> { 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, mut c: impl crate::capability::Capabilities) -> std::io::Result> { + pub fn class(&self, class: impl AsRef, mut c: impl crate::capability::Capabilities) -> std::io::Result> { 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) -> Option {