Add basic and power supply sysfs classes

This commit is contained in:
NGnius 2023-08-01 11:32:04 -04:00
parent e4579b6998
commit 346341b804
9 changed files with 715 additions and 8 deletions

View file

@ -1,5 +1,5 @@
[package]
name = "sysfs-nav"
name = "sysfuss"
version = "0.1.0"
edition = "2021"

59
src/attribute.rs Normal file
View file

@ -0,0 +1,59 @@
use std::path::PathBuf;
use std::io::Result as IoResult;
use std::str::FromStr;
use crate::EitherErr2;
/// sysfs entity attribute file functionality
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)
}
}
impl SysAttribute for String {
fn filename(&self) -> PathBuf {
PathBuf::from(self)
}
}
/// sysfs entity attribute functionality extension
pub trait SysAttributeExt: SysAttribute {
/// read attribute as string
fn read_str(&self, entity: &dyn crate::SysEntity) -> 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<()> {
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>> {
let s = self.read_str(entity).map_err(EitherErr2::First)?;
T::from_str(&s).map_err(EitherErr2::Second)
}
}
impl <X: SysAttribute> SysAttributeExt for X {}

91
src/basic.rs Normal file
View file

@ -0,0 +1,91 @@
use std::path::{Path, PathBuf};
use std::convert::AsRef;
use std::io::Result as IoResult;
const SYS_CLASS_PATH: &str = "sys/class";
/// Generic entity path with basic functionality
pub struct BasicEntityPath {
path: PathBuf
}
impl BasicEntityPath {
/// Create a new BasicEntityPath for manipulating path
pub fn new(path: impl AsRef<Path>) -> Self {
Self {
path: path.as_ref().to_path_buf(),
}
}
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);
dir_path.read_dir()
.map(
|iter| iter.filter_map(
|entry| entry.map(
|entry| if entry.path().is_file() { None } else { Some(Self::new(entry.path())) }
).transpose()))
}
/// Filter out attributes that do not exist on this entity
pub fn filter_capabilities<A: crate::SysAttribute>(&self, attributes: impl Iterator<Item=A>) -> impl Iterator<Item=A> {
let found_attrs: Vec<PathBuf> = if let Ok(dir_iter) = self.path.read_dir() {
dir_iter.filter_map(
|entry| entry.ok().filter(|entry| entry.path().is_file()).map(|entry| PathBuf::from(entry.file_name()))
).collect()
} else {
Vec::with_capacity(0)
};
attributes.filter(move |attr| found_attrs.contains(&attr.filename()))
}
}
impl AsRef<Path> for BasicEntityPath {
fn as_ref(&self) -> &Path {
self.path.as_path()
}
}
impl crate::SysEntity for BasicEntityPath {
fn to_entity_path(self) -> crate::EntityPath {
crate::EntityPath::Generic(self)
}
fn name(&self) -> IoResult<String> {
if let Some(s) = self.path.file_name().map(|name| name.to_str().map(|s| s.to_owned())).flatten() {
Ok(s)
} else {
Err(std::io::Error::from_raw_os_error(std::io::ErrorKind::InvalidInput as _))
}
}
}
impl crate::SysEntityAttributes<String, String, <String as std::str::FromStr>::Err> for BasicEntityPath {
fn capabilities(&self) -> Vec<String> {
if let Ok(dir_iter) = self.path.read_dir() {
dir_iter.filter_map(
|entry| entry.ok().filter(|entry| entry.path().is_file()).and_then(|entry| entry.file_name().to_str().map(|s| s.to_owned()))
).collect()
} else {
Vec::with_capacity(0)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::SysEntityRawExt;
#[test]
fn basic_entity_all() -> std::io::Result<()> {
let sys = crate::SysPath::default();
let all_basic: Vec<_> = BasicEntityPath::all(sys, "drm")?.collect();
assert!(!all_basic.is_empty());
for ent in all_basic.into_iter() {
assert!(ent?.attribute_str("uevent")? != "");
}
Ok(())
}
}

107
src/entity.rs Normal file
View file

@ -0,0 +1,107 @@
use std::path::Path;
use std::convert::AsRef;
use std::io::Result as IoResult;
use crate::EitherErr2;
use crate::SysAttributeExt;
/// sysfs class entity functionality
pub trait SysEntity: AsRef<Path> {
/// Convert specialized entity into general entity path container
fn to_entity_path(self) -> EntityPath;
/// Get the entity's name
fn name(&self) -> IoResult<String>;
}
/// 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>>;
/// Get an attribute by filename in the entity's directory
fn attribute_str<A: AsRef<Path>>(&self, attr: A) -> IoResult<String>;
/// Returns true when the entity has the attribute
fn exists<A: crate::SysAttribute>(&self, attr: A) -> bool;
/// Returns true when the entity has the attribute
fn exists_str<A: AsRef<Path>>(&self, attr: A) -> bool;
}
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>> {
attr.parse(self)
}
fn attribute_str<A: AsRef<Path>>(&self, attr: A) -> IoResult<String> {
std::fs::read_to_string(self.as_ref().join(attr))
}
fn exists_str<A: AsRef<Path>>(&self, attr: A) -> bool {
self.as_ref().join(attr).exists()
}
fn exists<A: crate::SysAttribute>(&self, attr: A) -> bool {
attr.exists(self)
}
}
/// sysfs class entity attribute type indicator
pub trait SysEntityAttributes<A: crate::SysAttribute, T: std::str::FromStr<Err=E>, E>: SysEntity + Sized {
/// Get an attribute on the entity
fn attribute(&self, attr: A) -> Result<T, EitherErr2<std::io::Error, E>> {
attr.parse(self)
}
/// Get attributes available on this entity;
fn capabilities(&self) -> Vec<A>;
}
/*pub trait SysEntityAttributesExt<A: crate::SysAttribute, T: std::str::FromStr<Err=E>, E>: SysEntityAttributes<A, T, E> {
fn attribute()
}*/
/// sysfs class entity implementors
pub enum EntityPath {
/// hwmon
HwMon(crate::HwMonPath),
/// power_supply
PowerSupply(crate::PowerSupplyPath),
/// Generic
Generic(crate::BasicEntityPath),
/// Miscellaneous
Custom(Box<dyn SysEntity>),
}
impl EntityPath {
fn inner(&self) -> &'_ dyn SysEntity {
match self {
Self::HwMon(inner) => inner,
Self::PowerSupply(inner) => inner,
Self::Generic(inner) => inner,
Self::Custom(inner) => inner.as_ref(),
}
}
}
impl AsRef<Path> for EntityPath {
fn as_ref(&self) -> &Path {
match self {
Self::HwMon(inner) => inner.as_ref(),
Self::PowerSupply(inner) => inner.as_ref(),
Self::Generic(inner) => inner.as_ref(),
Self::Custom(inner) => inner.as_ref().as_ref(),
}
}
}
impl SysEntity for EntityPath {
fn to_entity_path(self) -> Self {
self
}
fn name(&self) -> IoResult<String> {
self.inner().name()
}
}

47
src/errors.rs Normal file
View file

@ -0,0 +1,47 @@
/// Multi-error convenience enum
#[derive(Debug)]
pub enum EitherErr2<T1, T2> {
/// Type 1
First(T1),
/// Type 2
Second(T2),
}
impl <T1, T2> EitherErr2<T1, T2> {
/// Is this the first error type?
pub fn is_type1(&self) -> bool {
matches!(self, Self::First(_))
}
/// Is this the second error type?
pub fn is_type2(&self) -> bool {
matches!(self, Self::Second(_))
}
}
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 {
match self {
Self::First(x) => write!(f, "{}", x),
Self::Second(x) => write!(f, "{}", x),
}
}
}
impl <T1: std::error::Error, T2: std::error::Error> std::error::Error for EitherErr2<T1, T2> {}
/// Value parse error for an enumeration.
/// This usually indicates an unexpected value (one without an enum variant) was received while parsing.
#[derive(Debug)]
pub struct ValueEnumError<'a> {
pub(crate) got: String,
pub(crate) allowed: &'a [&'a str],
}
impl <'a> std::fmt::Display for ValueEnumError<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Got {}, expected one of {:?}", self.got, self.allowed)
}
}
impl <'a> std::error::Error for ValueEnumError<'a> {}

View file

@ -6,12 +6,12 @@ const HWMON_DIR_PATH: &'static str = "sys/class/hwmon/";
/// Attribute representation.
/// Attribute files are usually in the format `<type><number>_<item>`.
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct HwMonAttribute {
inner: HwMonAttributeInner,
}
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Eq)]
enum HwMonAttributeInner {
Name,
Standard {
@ -22,8 +22,30 @@ enum HwMonAttributeInner {
Uevent,
}
impl std::str::FromStr for HwMonAttributeInner {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"name" => Ok(Self::Name),
"uevent" => Ok(Self::Uevent),
s => {
let start = HwMonAttributeType::from_attr_start(s)?;
let end = HwMonAttributeItem::from_attr_end(s)?;
let trimmed = s.trim_start_matches(start.to_attr_str()).trim_end_matches(end.to_attr_str());
if trimmed.ends_with("_") {
let index = trimmed.trim_end_matches('_').parse().map_err(|e| format!("Invalid number in attribute name {}: {}", s, e))?;
Ok(Self::Standard { ty: start, number: index, item: end })
} else {
Err(format!("Missing underscore in attribute name {}", s))
}
}
}
}
}
/// Attribute type in the format `<type><number>_<item>`
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum HwMonAttributeType {
/// Voltage
In,
@ -36,7 +58,7 @@ pub enum HwMonAttributeType {
}
impl HwMonAttributeType {
fn to_attr_str(&self) -> &str {
const fn to_attr_str(&self) -> &str {
match self {
Self::In => "in",
Self::Temp => "out",
@ -44,10 +66,24 @@ impl HwMonAttributeType {
Self::Curr => "curr",
}
}
fn from_attr_start(s: &str) -> Result<Self, String> {
if s.starts_with(Self::In.to_attr_str()) {
Ok(Self::In)
} else if s.starts_with(Self::Temp.to_attr_str()) {
Ok(Self::Temp)
} else if s.starts_with(Self::Fan.to_attr_str()) {
Ok(Self::Fan)
} else if s.starts_with(Self::Curr.to_attr_str()) {
Ok(Self::Curr)
} else {
Err(format!("Unrecognised hwmon attribute type in name {}", s))
}
}
}
/// Attribute item in the format `<type><number>_<item>`
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum HwMonAttributeItem {
/// Input
Input,
@ -58,13 +94,25 @@ pub enum HwMonAttributeItem {
}
impl HwMonAttributeItem {
fn to_attr_str(&self) -> &str {
const fn to_attr_str(&self) -> &str {
match self {
Self::Input => "input",
Self::Min => "min",
Self::Max => "max",
}
}
fn from_attr_end(s: &str) -> Result<Self, String> {
if s.ends_with(Self::Input.to_attr_str()) {
Ok(Self::Input)
} else if s.ends_with(Self::Min.to_attr_str()) {
Ok(Self::Min)
} else if s.ends_with(Self::Max.to_attr_str()) {
Ok(Self::Max)
} else {
Err(format!("Unrecognised hwmon attribute type in name {}", s))
}
}
}
impl HwMonAttribute {
@ -97,6 +145,13 @@ impl HwMonAttribute {
}
}
}
impl crate::SysAttribute for HwMonAttribute {
fn filename(&self) -> PathBuf {
PathBuf::from(self.to_attr_str())
}
}
/// hwmon<number>/ directory
pub struct HwMonPath {
path: PathBuf
@ -161,10 +216,33 @@ impl AsRef<Path> for HwMonPath {
}
}
impl crate::SysEntity for HwMonPath {
fn to_entity_path(self) -> crate::EntityPath {
crate::EntityPath::HwMon(self)
}
fn name(&self) -> IoResult<String> {
self.attribute(HwMonAttribute::name())
}
}
impl crate::SysEntityAttributes<HwMonAttribute, String, <String as std::str::FromStr>::Err> for HwMonPath {
fn capabilities(&self) -> Vec<HwMonAttribute> {
if let Ok(dir_iter) = self.path.read_dir() {
dir_iter.filter_map(
|entry| entry.ok().filter(|entry| entry.path().is_file()).and_then(|entry| entry.file_name().into_string().ok()).and_then(|s| s.parse::<HwMonAttributeInner>().ok())
).map(|inner| HwMonAttribute {inner}).collect()
} else {
Vec::with_capacity(0)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::SysEntityAttributes;
#[test]
fn hwmon_all() -> std::io::Result<()> {
@ -172,7 +250,10 @@ mod tests {
let all_hwmon: Vec<_> = HwMonPath::all(sys)?.collect();
assert!(!all_hwmon.is_empty());
for hwmon in all_hwmon.into_iter() {
assert!(hwmon?.attribute(HwMonAttribute::name())? != "");
let hwmon = hwmon?;
assert!(hwmon.attribute(HwMonAttribute::name())? != "");
assert!(!hwmon.capabilities().is_empty());
assert!(hwmon.capabilities().contains(&HwMonAttribute::name()))
}
Ok(())
}

View file

@ -4,9 +4,24 @@
pub(crate) const DEFAULT_ROOT: &'static str = "/";
mod attribute;
pub use attribute::{SysAttribute, SysAttributeExt};
mod basic;
pub use basic::BasicEntityPath;
mod entity;
pub use entity::{EntityPath, SysEntity, SysEntityRawExt, SysEntityAttributes};
mod errors;
pub use errors::{EitherErr2, ValueEnumError};
mod hwmon;
pub use hwmon::{HwMonPath, HwMonAttribute, HwMonAttributeType, HwMonAttributeItem};
mod power_supply;
pub use power_supply::{PowerSupplyPath, PowerSupplyType, PowerSupplyAttribute};
mod syspath;
pub use syspath::SysPath;

292
src/power_supply.rs Normal file
View file

@ -0,0 +1,292 @@
use std::path::{Path, PathBuf};
use std::convert::AsRef;
use std::io::Result as IoResult;
use crate::SysEntityRawExt;
//use crate::SysEntityAttributes;
const PSU_DIR_PATH: &'static str = "sys/class/power_supply/";
const CLASS_PATH: &'static str = "power_supply";
/// Attribute files in a power supply directory
/// These attributes are taken from https://github.com/torvalds/linux/blob/master/Documentation/ABI/testing/sysfs-class-power
#[derive(Debug, PartialEq, Eq)]
pub enum PowerSupplyAttribute {
/// Reports the name of the device manufacturer.
Manufacturer,
/// Reports the name of the device model.
ModelName,
/// Reports the serial number of the device.
SerialNumber,
/// Describes the main type of the supply.
Type,
/// Reports an average current reading over a fixed period (uA).
CurrentAverage,
/// Reports the maximum current the power supply supports (uA).
CurrentMax,
/// Reports the instantaneous current of the power supply (uA).
CurrentNow,
/// Reports the temperature reading (dC -- 1/10 degree Celcius).
Temperature,
/// Maximum temperature where the supply will notify user-space of the event (dC).
TemperatureAlertMax,
/// Minimum temperature where the supply will notify user-space of the event (dC).
TemperatureAlertMin,
/// Reports the maximum allowed temperature for operation (dC).
TemperatureMax,
/// Reports the minimum allowed temperature for operation (dC).
TemperatureMin,
/// Reports the maximum safe voltage (uV).
VoltageMax,
/// Reports the minimum safe voltage (uV).
VoltageMin,
/// Reports the instantaneous voltage of the power supply (uV).
VoltageNow,
/// Fine grain representation of the battery capacity (%).
Capacity,
/// Maximum battery capacity trip-wire value where the supply will notify user-space of the event (%).
CapacityAlertMax,
/// Minimum battery capacity trip-wire value where the supply will notify user-space of the event (%).
CapacityAlertMin,
/// This values provides the maximum error margin expected to exist by the fuel gauge in percent (%).
CapacityErrorMargin,
/// Coarse representation of battery capacity ("Unknown", "Critical", "Low", "Normal", "High", or "Full").
CapacityLevel,
/// Maximum allowable charging current (uA).
ChargeControlLimit,
/// Maximum legal value for the charge_control_limit property (uA).
ChargeControlLimitMax,
/// Represents a battery percentage level, below which charging will begin (%).
ChargeControlStartThreshold,
/// Represents a battery percentage level, above which charging will stop (%).
ChargeControlEndThreshold,
/// Represents the type of charging currently being applied to the battery ("Unknown", "N/A", "Trickle", "Fast", "Standard", "Adaptive", "Custom", "Long Life", or "Bypass").
ChargeType,
/// Reports the charging current value which is used to determine when the battery is considered full and charging should end (uA).
ChargeTermCurrent,
/// Reports the health of the battery or battery side of charger functionality ( "Unknown", "Good", "Overheat", "Dead", "Over voltage", "Unspecified failure", "Cold", "Watchdog timer expire", "Safety timer expire", "Over current", "Calibration required", "Warm", "Cool", "Hot", or "No battery").
Health,
/// Reports the charging current applied during pre-charging phase for a battery charge cycle (uA).
PrechargeCurrent,
/// Reports whether a battery is present or not in the system (0 or 1). If the property does not exist, the battery is considered to be present.
Present,
/// Represents the charging status of the battery ( "Unknown", "Charging", "Discharging", "Not charging", or "Full").
Status,
/// Represents the charging behaviour ("auto", "inhibit-charge", or "force-discharge").
ChargeBehaviour,
/// Describes the battery technology supported by the supply.
Technology,
/// Reports an average voltage over a fixed period (uV).
VoltageAverage,
/// Reports the number of full charge + discharge cycles the battery has undergone.
CycleCount,
// TODO USB properties
}
impl crate::SysAttribute for PowerSupplyAttribute {
fn filename(&self) -> PathBuf {
let s = match self {
Self::Manufacturer => "manufacturer",
Self::ModelName => "model_name",
Self::SerialNumber => "serial_number",
Self::Type => "type",
Self::CurrentAverage => "current_avg",
Self::CurrentMax => "current_max",
Self::CurrentNow => "current_now",
Self::Temperature => "temp",
Self::TemperatureAlertMax => "temp_alert_max",
Self::TemperatureAlertMin => "temp_alert_min",
Self::TemperatureMax => "temp_max",
Self::TemperatureMin => "temp_min",
Self::VoltageMax => "voltage_max",
Self::VoltageMin => "voltage_min",
Self::VoltageNow => "voltage_now",
Self::Capacity => "capacity",
Self::CapacityAlertMax => "capacity_alert_max",
Self::CapacityAlertMin => "capacity_alert_min",
Self::CapacityErrorMargin => "capacity_error_margin",
Self::CapacityLevel => "capacity_level",
Self::ChargeControlLimit => "charge_control_limit",
Self::ChargeControlLimitMax => "charge_control_limit_max",
Self::ChargeControlStartThreshold => "charge_control_start_threshold",
Self::ChargeControlEndThreshold => "charge_control_end_threshold",
Self::ChargeType => "charge_type",
Self::ChargeTermCurrent => "charge_term_current",
Self::Health => "health",
Self::PrechargeCurrent => "precharge_current",
Self::Present => "present",
Self::Status => "status",
Self::ChargeBehaviour => "charge_behaviour",
Self::Technology => "technology",
Self::VoltageAverage => "voltage_avg",
Self::CycleCount => "cycle_count",
};
s.into()
}
}
/// power_supply/<entity>/type attribute value
#[derive(Debug, PartialEq, Eq)]
pub enum PowerSupplyType {
/// Battery
Battery,
/// UPS
UPS,
/// Mains
Mains,
/// USB
USB,
/// Wireless
Wireless,
}
impl PowerSupplyType {
fn allowed_values() -> &'static [&'static str] {
&[
"Battery",
"UPS",
"Mains",
"USB",
"Wireless",
]
}
}
impl std::fmt::Display for PowerSupplyType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Battery => write!(f, "Battery"),
Self::UPS => write!(f, "UPS"),
Self::Mains => write!(f, "Mains"),
Self::USB => write!(f, "USB"),
Self::Wireless => write!(f, "Wireless")
}
}
}
impl std::str::FromStr for PowerSupplyType {
type Err = crate::ValueEnumError<'static>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Battery" => Ok(Self::Battery),
"UPS" => Ok(Self::UPS),
"Mains" => Ok(Self::Mains),
"USB" => Ok(Self::USB),
"Wireless" => Ok(Self::Wireless),
other => Err(crate::ValueEnumError {
got: other.to_owned(),
allowed: Self::allowed_values(),
})
}
}
}
/// power_supply/<entity>/ directory
pub struct PowerSupplyPath {
inner: crate::BasicEntityPath,
}
impl PowerSupplyPath {
pub(crate) fn all(root: impl AsRef<Path>) -> IoResult<impl Iterator<Item=IoResult<Self>>> {
Ok(crate::BasicEntityPath::all(root, CLASS_PATH)?
.map(|result| result.map(|inner| Self { inner })))
}
/// Get a power_supply entry by directory name
pub(crate) fn name(root: &crate::SysPath, name: &str) -> Self {
Self {
inner: crate::BasicEntityPath::new(root.as_ref().join(PSU_DIR_PATH).join(name))
}
}
/// Get the power supply type
pub fn type_str(&self) -> IoResult<String> {
self.attribute_str("type")
}
/// Get the power supply type
pub fn type_enum(&self) -> Result<PowerSupplyType, crate::EitherErr2<std::io::Error, <PowerSupplyType as std::str::FromStr>::Err>> {
self.attribute(PowerSupplyAttribute::Type)
}
}
impl AsRef<Path> for PowerSupplyPath {
fn as_ref(&self) -> &Path {
self.inner.as_ref()
}
}
impl crate::SysEntity for PowerSupplyPath {
fn to_entity_path(self) -> crate::EntityPath {
crate::EntityPath::PowerSupply(self)
}
fn name(&self) -> IoResult<String> {
self.inner.name()
}
}
impl crate::SysEntityAttributes<PowerSupplyAttribute, String, <String as std::str::FromStr>::Err> for PowerSupplyPath {
fn capabilities(&self) -> Vec<PowerSupplyAttribute> {
self.inner.filter_capabilities(vec![
PowerSupplyAttribute::Manufacturer,
PowerSupplyAttribute::ModelName,
PowerSupplyAttribute::SerialNumber,
PowerSupplyAttribute::Type,
PowerSupplyAttribute::CurrentAverage,
PowerSupplyAttribute::CurrentMax,
PowerSupplyAttribute::CurrentNow,
PowerSupplyAttribute::Temperature,
PowerSupplyAttribute::TemperatureAlertMax,
PowerSupplyAttribute::TemperatureAlertMin,
PowerSupplyAttribute::TemperatureMax,
PowerSupplyAttribute::TemperatureMin,
PowerSupplyAttribute::VoltageMax,
PowerSupplyAttribute::VoltageMin,
PowerSupplyAttribute::VoltageNow,
PowerSupplyAttribute::Capacity,
PowerSupplyAttribute::CapacityAlertMax,
PowerSupplyAttribute::CapacityAlertMin,
PowerSupplyAttribute::CapacityErrorMargin,
PowerSupplyAttribute::CapacityLevel,
PowerSupplyAttribute::ChargeControlLimit,
PowerSupplyAttribute::ChargeControlLimitMax,
PowerSupplyAttribute::ChargeControlStartThreshold,
PowerSupplyAttribute::ChargeControlEndThreshold,
PowerSupplyAttribute::ChargeType,
PowerSupplyAttribute::ChargeTermCurrent,
PowerSupplyAttribute::Health,
PowerSupplyAttribute::PrechargeCurrent,
PowerSupplyAttribute::Present,
PowerSupplyAttribute::Status,
PowerSupplyAttribute::ChargeBehaviour,
PowerSupplyAttribute::Technology,
PowerSupplyAttribute::VoltageAverage,
PowerSupplyAttribute::CycleCount,
].into_iter()).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::SysEntityAttributes;
#[test]
fn power_supply_all() -> std::io::Result<()> {
let sys = crate::SysPath::default();
let all_psu: Vec<_> = PowerSupplyPath::all(sys)?.collect();
assert!(!all_psu.is_empty());
for p in all_psu.into_iter() {
let p = p?;
assert!(p.type_str()? != "");
assert!(!p.capabilities().is_empty());
assert!(p.capabilities().contains(&PowerSupplyAttribute::Type))
}
Ok(())
}
}

View file

@ -1,6 +1,8 @@
use std::path::{Path, PathBuf};
use std::convert::AsRef;
use crate::SysEntity;
/// sysfs root
pub struct SysPath {
root: PathBuf,
@ -15,6 +17,14 @@ impl SysPath {
}
}
/// Get all entities in the system
pub fn entities(&self) -> std::io::Result<impl Iterator<Item=std::io::Result<crate::EntityPath>> + '_> {
Ok(crate::HwMonPath::all(self)?
.map(|entity| entity.map(|entity| entity.to_entity_path()))
.chain(crate::PowerSupplyPath::all(self)?
.map(|entity| entity.map(|entity| entity.to_entity_path()))))
}
/// Find a hardware monitor entry by name
pub fn hwmon_by_name(&self, name: &str) -> std::io::Result<crate::HwMonPath> {
match crate::HwMonPath::name(self, name) {
@ -30,6 +40,11 @@ impl SysPath {
entry.attribute(crate::HwMonAttribute::name())?;
Ok(entry)
}
/// Find a power supply entry by name
pub fn power_supply(&self, name: &str) -> crate::PowerSupplyPath {
crate::PowerSupplyPath::name(self, name)
}
}
impl std::default::Default for SysPath {