1use std::path::{Path, PathBuf};
2use std::convert::AsRef;
3use std::io::Result as IoResult;
4
5const HWMON_DIR_PATH: &'static str = "sys/class/hwmon/";
6
7use crate::SysEntityAttributesExt;
8
9#[derive(Clone, Copy, PartialEq, Eq)]
12#[cfg_attr(feature = "derive", derive(Debug))]
13pub struct HwMonAttribute {
14 inner: HwMonAttributeInner,
15}
16
17#[derive(Clone, Copy, PartialEq, Eq)]
18#[cfg_attr(feature = "derive", derive(Debug))]
19enum HwMonAttributeInner {
20 Name,
21 Standard {
22 ty: HwMonAttributeType,
23 number: u64,
24 item: HwMonAttributeItem,
25 },
26 Uevent,
27 Custom(&'static str),
28}
29
30impl std::str::FromStr for HwMonAttributeInner {
31 type Err = String;
32
33 fn from_str(s: &str) -> Result<Self, Self::Err> {
34 match s {
35 "name" => Ok(Self::Name),
36 "uevent" => Ok(Self::Uevent),
37 s => {
38 let start = HwMonAttributeType::from_attr_start(s)?;
39 let end = HwMonAttributeItem::from_attr_end(s)?;
40 let trimmed = s.trim_start_matches(start.to_attr_str()).trim_end_matches(end.to_attr_str());
41 if trimmed.ends_with("_") {
42 let index = trimmed.trim_end_matches('_').parse().map_err(|e| format!("Invalid number in attribute name {}: {}", s, e))?;
43 Ok(Self::Standard { ty: start, number: index, item: end })
44 } else {
45 Err(format!("Missing underscore in attribute name {}", s))
46 }
47 }
48 }
49 }
50}
51
52#[derive(Clone, Copy, PartialEq, Eq)]
54#[cfg_attr(feature = "derive", derive(Debug))]
55pub enum HwMonAttributeType {
56 In,
58 Temp,
60 Fan,
62 Curr,
64}
65
66impl HwMonAttributeType {
67 const fn to_attr_str(&self) -> &str {
68 match self {
69 Self::In => "in",
70 Self::Temp => "out",
71 Self::Fan => "fan",
72 Self::Curr => "curr",
73 }
74 }
75
76 fn from_attr_start(s: &str) -> Result<Self, String> {
77 if s.starts_with(Self::In.to_attr_str()) {
78 Ok(Self::In)
79 } else if s.starts_with(Self::Temp.to_attr_str()) {
80 Ok(Self::Temp)
81 } else if s.starts_with(Self::Fan.to_attr_str()) {
82 Ok(Self::Fan)
83 } else if s.starts_with(Self::Curr.to_attr_str()) {
84 Ok(Self::Curr)
85 } else {
86 Err(format!("Unrecognised hwmon attribute type in name {}", s))
87 }
88 }
89}
90
91#[derive(Clone, Copy, PartialEq, Eq)]
93#[cfg_attr(feature = "derive", derive(Debug))]
94pub enum HwMonAttributeItem {
95 Input,
97 Min,
99 Max,
101 Label,
103}
104
105impl HwMonAttributeItem {
106 const fn to_attr_str(&self) -> &str {
107 match self {
108 Self::Input => "input",
109 Self::Min => "min",
110 Self::Max => "max",
111 Self::Label => "label"
112 }
113 }
114
115 fn from_attr_end(s: &str) -> Result<Self, String> {
116 if s.ends_with(Self::Input.to_attr_str()) {
117 Ok(Self::Input)
118 } else if s.ends_with(Self::Min.to_attr_str()) {
119 Ok(Self::Min)
120 } else if s.ends_with(Self::Max.to_attr_str()) {
121 Ok(Self::Max)
122 } else {
123 Err(format!("Unrecognised hwmon attribute type in name {}", s))
124 }
125 }
126}
127
128impl HwMonAttribute {
129 pub const fn name() -> Self {
131 Self {
132 inner: HwMonAttributeInner::Name
133 }
134 }
135
136 pub const fn new(ty: HwMonAttributeType, number: u64, item: HwMonAttributeItem) -> Self {
138 Self {
139 inner: HwMonAttributeInner::Standard { ty, number, item }
140 }
141 }
142
143 pub const fn uevent() -> Self {
145 Self {
146 inner: HwMonAttributeInner::Uevent
147 }
148 }
149
150 pub const fn custom(attr: &'static str) -> Self {
152 Self {
153 inner: HwMonAttributeInner::Custom(attr)
154 }
155 }
156
157 fn to_attr_str(&self) -> String {
158 match self.inner {
159 HwMonAttributeInner::Name => "name".to_string(),
160 HwMonAttributeInner::Standard { ty, number, item } => format!("{}{}_{}", ty.to_attr_str(), number, item.to_attr_str()),
161 HwMonAttributeInner::Uevent => "uevent".to_string(),
162 HwMonAttributeInner::Custom(s) => s.to_owned(),
163 }
164 }
165}
166
167impl crate::SysAttribute for HwMonAttribute {
168 fn filename(&self) -> PathBuf {
169 PathBuf::from(self.to_attr_str())
170 }
171}
172
173#[cfg_attr(feature = "derive", derive(Debug, Clone))]
175pub struct HwMonPath {
176 path: PathBuf
177}
178
179impl HwMonPath {
180 pub(crate) fn all(root: impl AsRef<Path>) -> IoResult<impl Iterator<Item=IoResult<Self>>> {
181 let hwmon_dir_path = root.as_ref().join(HWMON_DIR_PATH);
182 hwmon_dir_path.read_dir()
183 .map(
184 |iter| iter.filter_map(
185 |entry| entry.map(
186 |entry| if crate::os_str_util::starts_with(&entry.file_name(), "hwmon".as_ref()) {
187 Some(Self {
188 path: entry.path(),
189 })
190 } else {
191 None
192 }).transpose()))
193 }
194
195 pub(crate) fn entry(root: &crate::SysPath, i: u64) -> Self {
198 Self {
199 path: root.as_ref().join(HWMON_DIR_PATH).join(format!("hwmon{}", i)),
200 }
201 }
202
203 pub(crate) fn name(root: &crate::SysPath, name: &str) -> IoResult<Option<Self>> {
205 for entry in Self::all(root)? {
206 let entry = entry?;
207 let value: String = entry.attribute::<String, _>(HwMonAttribute::name()).map_err(|e| match e {
208 crate::EitherErr2::First(e) => e,
209 crate::EitherErr2::Second(_e) => panic!("Infallible"),
210 })?;
211 if value == name {
212 return Ok(Some(entry))
213 }
214 }
215 Ok(None)
216 }
217
218 pub fn attribute_str(&self, name: &str) -> IoResult<String> {
221 std::fs::read_to_string(self.path.join(name))
222 }
223
224 pub fn path_to(&self, attr: HwMonAttribute) -> PathBuf {
227 self.path.join(&attr.to_attr_str())
228 }
229}
230
231impl AsRef<Path> for HwMonPath {
232 fn as_ref(&self) -> &Path {
233 self.path.as_path()
234 }
235}
236
237impl crate::SysEntity for HwMonPath {
238 fn to_entity_path(self) -> crate::EntityPath {
239 crate::EntityPath::HwMon(self)
240 }
241
242 fn name(&self) -> IoResult<String> {
243 self.attribute(HwMonAttribute::name()).map_err(|e| e.map_infallible_second())
244 }
245}
246
247impl crate::SysEntityAttributes<HwMonAttribute> for HwMonPath {
248 fn capabilities(&self) -> Vec<HwMonAttribute> {
249 if let Ok(dir_iter) = self.path.read_dir() {
250 dir_iter.filter_map(
251 |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())
252 ).map(|inner| HwMonAttribute {inner}).collect()
253 } else {
254 Vec::with_capacity(0)
255 }
256 }
257}
258
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263 use crate::SysEntityAttributes;
264
265 #[test]
266 fn hwmon_all() -> std::io::Result<()> {
267 let sys = crate::SysPath::default();
268 let all_hwmon: Vec<_> = HwMonPath::all(sys)?.collect();
269 assert!(!all_hwmon.is_empty());
270 for hwmon in all_hwmon.into_iter() {
271 let hwmon = hwmon?;
272 assert!(hwmon.attribute::<String, _>(HwMonAttribute::name()).map_err(|e| e.map_infallible_second())? != "");
273 assert!(!hwmon.attribute::<String, _>(HwMonAttribute::name()).map_err(|e| e.map_infallible_second())?.ends_with("\n"));
274 assert!(!hwmon.capabilities().is_empty());
275 assert!(hwmon.capabilities().contains(&HwMonAttribute::name()))
276 }
277 Ok(())
278 }
279
280 #[test]
281 fn hwmon_capabilities() -> std::io::Result<()> {
282 let sys = crate::SysPath::default();
283 if !sys.hwmon_by_name("amdgpu").is_ok() {
284 eprintln!("hwmon test skipped since amdgpu does not exist (maybe running on a laptop PC?)");
286 return Ok(())
287 }
288 let hwmon = sys.hwmon(crate::capability::attributes([
289 HwMonAttribute::name(),
290 HwMonAttribute::new(HwMonAttributeType::Fan, 1, HwMonAttributeItem::Input),
291 HwMonAttribute::new(HwMonAttributeType::Fan, 1, HwMonAttributeItem::Min),
292 HwMonAttribute::new(HwMonAttributeType::Fan, 1, HwMonAttributeItem::Max)
293 ].into_iter()))?.next().expect("Missing capable amdgpu");
294 assert_eq!(hwmon.attribute::<String, _>(HwMonAttribute::name()).expect("name capable but also incapable"), "amdgpu");
295 Ok(())
296 }
297}