use std::path::{Path, PathBuf};
use std::convert::AsRef;
use std::io::Result as IoResult;
const SYS_CLASS_PATH: &str = "sys/class";
#[cfg_attr(feature = "derive", derive(Debug, Clone))]
pub struct BasicEntityPath {
path: PathBuf
}
impl BasicEntityPath {
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);
Ok(dir_path.read_dir()?
.filter_map(
|entry| entry.map(
|entry| if entry.path().is_file() { None } else { Some(Self::new(entry.path())) }
).transpose()))
}
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> 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::SysEntity;
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(())
}
#[test]
fn basic_capabilities() -> std::io::Result<()> {
let sys = crate::SysPath::default();
if std::fs::read_to_string("/sys/class/hwmon/hwmon0/name")?.trim() != "nvme" {
eprintln!("basic entity test skipped since hwmon0 is not nvme (maybe running on a different PC?)");
return Ok(())
}
let basic = sys.class("hwmon", crate::capability::attributes([
"name".to_string(),
"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");
println!("Attribute ./name = '{}'", attr_value);
assert!(attr_value == "nvme");
Ok(())
}
#[test]
fn basic_drm_capabilities() -> std::io::Result<()> {
let sys = crate::SysPath::default();
let expected_dev = if let Ok(dev) = std::fs::read_to_string("/sys/class/drm/card1/dev") {
dev.trim().to_owned()
} else {
eprintln!("basic entity test skipped since drm card0 does not exist (maybe running on a different PC?)");
return Ok(())
};
let basic = sys.class("drm", crate::capability::attributes([
"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");
println!("Attribute ./dev = '{}'", attr_value);
assert!(attr_value == expected_dev);
Ok(())
}
}