#![deny(
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts
)]
#![forbid(unsafe_code, unstable_features)]
pub mod hwmon;
pub mod sensors;
pub mod units;
mod parsing;
pub use hwmon::Hwmon;
pub use parsing::Error as ParsingError;
use hwmon::*;
pub(crate) use parsing::{Parseable, Result as ParsingResult};
use std::iter::FusedIterator;
use std::path::{Path, PathBuf};
const HWMON_PATH: &str = "/sys/class/hwmon/";
#[derive(Debug, Clone)]
pub struct Hwmons<H: Hwmon> {
path: PathBuf,
hwmons: Vec<H>,
}
impl<H: Hwmon> Hwmons<H> {
pub fn path(&self) -> &Path {
&self.path
}
pub fn hwmons_by_name(&self, name: impl AsRef<str>) -> impl Iterator<Item = &H> {
self.hwmons
.iter()
.filter(move |hwmon| hwmon.name() == name.as_ref())
}
pub fn hwmon_by_index(&self, index: usize) -> Option<&H> {
self.hwmons.get(index)
}
pub fn iter(&self) -> Iter<'_, H> {
Iter {
index: 0,
hwmons: &self.hwmons,
}
}
fn parse(path: impl AsRef<Path>) -> ParsingResult<Self>
where
H: Parseable<Parent = Self>,
{
let path = path.as_ref();
if !path.exists() {
return Err(ParsingError::PathDoesNotExist {
path: path.to_path_buf(),
});
}
if !path.is_dir() {
return Err(ParsingError::InvalidPath {
path: path.to_path_buf(),
});
}
let mut hwmons = Hwmons {
path: path.to_path_buf(),
hwmons: Vec::new(),
};
for index in 0.. {
match H::parse(&hwmons, index) {
Ok(hwmon) => {
hwmons.hwmons.push(hwmon);
}
Err(e) => match e {
ParsingError::PathDoesNotExist { .. } => break,
e => return Err(e),
},
}
}
Ok(hwmons)
}
}
impl Hwmons<ReadOnlyHwmon> {
pub fn parse_read_only() -> ParsingResult<Self> {
Self::parse(HWMON_PATH)
}
}
#[cfg(feature = "writable")]
impl Hwmons<ReadWriteHwmon> {
pub fn parse_read_write() -> ParsingResult<Self> {
Self::parse(HWMON_PATH)
}
#[cfg(feature = "unrestricted_parsing")]
pub fn parse_path(path: impl AsRef<Path>) -> ParsingResult<Self> {
Self::parse(path)
}
}
#[derive(Debug, Copy, Clone)]
pub struct Iter<'a, H: Hwmon> {
hwmons: &'a [H],
index: usize,
}
impl<'a, H: Hwmon> Iterator for Iter<'a, H> {
type Item = (usize, &'a str, &'a H);
fn next(&mut self) -> Option<Self::Item> {
if let Some(hwmon) = self.hwmons.get(self.index) {
self.index += 1;
return Some((self.index - 1, hwmon.name(), hwmon));
}
None
}
}
impl<'a, H: Hwmon> FusedIterator for Iter<'a, H> {}
impl<'a, H: Hwmon> ExactSizeIterator for Iter<'a, H> {
fn len(&self) -> usize {
self.hwmons.len() - self.index
}
}
impl<'a, H: Hwmon> IntoIterator for &'a Hwmons<H> {
type Item = (usize, &'a str, &'a H);
type IntoIter = Iter<'a, H>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use std::fs::{remove_dir_all, File, OpenOptions};
use std::io::Write;
use std::path::{Path, PathBuf};
pub struct VirtualHwmonBuilder {
root: PathBuf,
index: u16,
}
impl VirtualHwmonBuilder {
pub fn create(
root: impl AsRef<Path>,
index: u16,
name: impl AsRef<[u8]>,
) -> VirtualHwmonBuilder {
let path = root.as_ref().join(format!("hwmon{}", index));
fs::create_dir_all(&path).unwrap();
File::create(path.join("name"))
.unwrap()
.write(name.as_ref())
.unwrap();
VirtualHwmonBuilder {
root: root.as_ref().to_path_buf(),
index,
}
}
pub fn add_temp(
self,
index: u16,
value: i32,
label: impl AsRef<str>,
) -> VirtualHwmonBuilder {
OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(self.path().join(format!("temp{}_input", index)))
.unwrap()
.write(value.to_string().as_bytes())
.unwrap();
OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(self.path().join(format!("temp{}_enable", index)))
.unwrap()
.write(b"1\n")
.unwrap();
OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(self.path().join(format!("temp{}_label", index)))
.unwrap()
.write(label.as_ref().as_bytes())
.unwrap();
self
}
pub fn add_fan(self, index: u16, value: u32) -> VirtualHwmonBuilder {
OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(self.path().join(format!("fan{}_input", index)))
.unwrap()
.write(value.to_string().as_bytes())
.unwrap();
OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(self.path().join(format!("fan{}_enable", index)))
.unwrap()
.write(b"1\n")
.unwrap();
self
}
pub fn add_pwm(
self,
index: u16,
create_enable_file: bool,
create_mode_file: bool,
) -> VirtualHwmonBuilder {
OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(self.path().join(&format!("pwm{}", index)))
.unwrap()
.write(b"0\n")
.unwrap();
if create_enable_file {
OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(self.path().join(&format!("pwm{}_enable", index)))
.unwrap()
.write(b"2\n")
.unwrap();
}
if create_mode_file {
OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(self.path().join(&format!("pwm{}_mode", index)))
.unwrap()
.write(b"1\n")
.unwrap();
}
self.add_fan(index, 1000)
}
pub fn path(&self) -> PathBuf {
self.root.join(format!("hwmon{}", self.index))
}
}
#[test]
fn test_parse() {
let test_path = Path::new("test_parse");
VirtualHwmonBuilder::create(test_path, 0, "system")
.add_pwm(1, true, true)
.add_pwm(2, true, true)
.add_temp(1, 40000, "temp1")
.add_temp(2, 60000, "temp2");
VirtualHwmonBuilder::create(test_path, 1, "other")
.add_pwm(1, true, true)
.add_temp(1, 40000, "temp1")
.add_fan(2, 1000);
let hwmons: Hwmons<ReadOnlyHwmon> = Hwmons::parse(test_path).unwrap();
let hwmon0 = hwmons.hwmons_by_name("system").next().unwrap();
let hwmon1 = hwmons.hwmons_by_name("other").next().unwrap();
assert_eq!(hwmon0.name(), hwmons.hwmon_by_index(0).unwrap().name());
assert_eq!(hwmon1.name(), hwmons.hwmon_by_index(1).unwrap().name());
assert_eq!(hwmons.hwmon_by_index(2).is_none(), true);
assert_eq!(hwmons.hwmons_by_name("alias").next().is_none(), true);
assert_eq!(hwmon0.temps().len(), 2);
assert_eq!(hwmon1.temps().len(), 1);
assert_eq!(hwmon0.pwms().len(), 2);
assert_eq!(hwmon1.pwms().len(), 1);
hwmon0.pwms().get(&1u16).unwrap();
hwmon0.pwms().get(&2u16).unwrap();
hwmon1.pwms().get(&1u16).unwrap();
hwmon0.temps().get(&1u16).unwrap();
hwmon0.temps().get(&2u16).unwrap();
hwmon1.temps().get(&1u16).unwrap();
remove_dir_all(test_path).unwrap();
}
}