use std::path::{Path, PathBuf};
use std::convert::AsRef;
use std::io::Result as IoResult;
const HWMON_DIR_PATH: &'static str = "sys/class/hwmon/";
use crate::SysEntityAttributesExt;
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "derive", derive(Debug))]
pub struct HwMonAttribute {
inner: HwMonAttributeInner,
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "derive", derive(Debug))]
enum HwMonAttributeInner {
Name,
Standard {
ty: HwMonAttributeType,
number: u64,
item: HwMonAttributeItem,
},
Uevent,
Custom(&'static str),
}
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))
}
}
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "derive", derive(Debug))]
pub enum HwMonAttributeType {
In,
Temp,
Fan,
Curr,
}
impl HwMonAttributeType {
const fn to_attr_str(&self) -> &str {
match self {
Self::In => "in",
Self::Temp => "out",
Self::Fan => "fan",
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))
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "derive", derive(Debug))]
pub enum HwMonAttributeItem {
Input,
Min,
Max,
Label,
}
impl HwMonAttributeItem {
const fn to_attr_str(&self) -> &str {
match self {
Self::Input => "input",
Self::Min => "min",
Self::Max => "max",
Self::Label => "label"
}
}
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 {
pub const fn name() -> Self {
Self {
inner: HwMonAttributeInner::Name
}
}
pub const fn new(ty: HwMonAttributeType, number: u64, item: HwMonAttributeItem) -> Self {
Self {
inner: HwMonAttributeInner::Standard { ty, number, item }
}
}
pub const fn uevent() -> Self {
Self {
inner: HwMonAttributeInner::Uevent
}
}
pub const fn custom(attr: &'static str) -> Self {
Self {
inner: HwMonAttributeInner::Custom(attr)
}
}
fn to_attr_str(&self) -> String {
match self.inner {
HwMonAttributeInner::Name => "name".to_string(),
HwMonAttributeInner::Standard { ty, number, item } => format!("{}{}_{}", ty.to_attr_str(), number, item.to_attr_str()),
HwMonAttributeInner::Uevent => "uevent".to_string(),
HwMonAttributeInner::Custom(s) => s.to_owned(),
}
}
}
impl crate::SysAttribute for HwMonAttribute {
fn filename(&self) -> PathBuf {
PathBuf::from(self.to_attr_str())
}
}
#[cfg_attr(feature = "derive", derive(Debug, Clone))]
pub struct HwMonPath {
path: PathBuf
}
impl HwMonPath {
pub(crate) fn all(root: impl AsRef<Path>) -> IoResult<impl Iterator<Item=IoResult<Self>>> {
let hwmon_dir_path = root.as_ref().join(HWMON_DIR_PATH);
hwmon_dir_path.read_dir()
.map(
|iter| iter.filter_map(
|entry| entry.map(
|entry| if crate::os_str_util::starts_with(&entry.file_name(), "hwmon".as_ref()) {
Some(Self {
path: entry.path(),
})
} else {
None
}).transpose()))
}
pub(crate) fn entry(root: &crate::SysPath, i: u64) -> Self {
Self {
path: root.as_ref().join(HWMON_DIR_PATH).join(format!("hwmon{}", i)),
}
}
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 {
crate::EitherErr2::First(e) => e,
crate::EitherErr2::Second(_e) => panic!("Infallible"),
})?;
if value == name {
return Ok(Some(entry))
}
}
Ok(None)
}
pub fn attribute_str(&self, name: &str) -> IoResult<String> {
std::fs::read_to_string(self.path.join(name))
}
pub fn path_to(&self, attr: HwMonAttribute) -> PathBuf {
self.path.join(&attr.to_attr_str())
}
}
impl AsRef<Path> for HwMonPath {
fn as_ref(&self) -> &Path {
self.path.as_path()
}
}
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()).map_err(|e| e.map_infallible_second())
}
}
impl crate::SysEntityAttributes<HwMonAttribute> 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<()> {
let sys = crate::SysPath::default();
let all_hwmon: Vec<_> = HwMonPath::all(sys)?.collect();
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.capabilities().is_empty());
assert!(hwmon.capabilities().contains(&HwMonAttribute::name()))
}
Ok(())
}
#[test]
fn hwmon_capabilities() -> std::io::Result<()> {
let sys = crate::SysPath::default();
if !sys.hwmon_by_name("amdgpu").is_ok() {
eprintln!("hwmon test skipped since amdgpu does not exist (maybe running on a laptop PC?)");
return Ok(())
}
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)
].into_iter()))?.next().expect("Missing capable amdgpu");
assert_eq!(hwmon.attribute::<String, _>(HwMonAttribute::name()).expect("name capable but also incapable"), "amdgpu");
Ok(())
}
}