1pub mod battery;
10pub mod btrfs;
11pub mod diskstats;
12pub mod hwmon;
13pub mod mdstat;
14pub mod meminfo;
15pub mod mounts;
16pub mod net;
17pub mod procstat;
18pub mod rapl;
19
20use std::fs;
21use std::path::{Path, PathBuf};
22
23#[derive(Debug, Clone)]
25pub struct SysRoots {
26 pub sys: PathBuf,
28 pub proc: PathBuf,
30}
31
32impl Default for SysRoots {
33 fn default() -> Self {
34 Self {
35 sys: PathBuf::from("/sys"),
36 proc: PathBuf::from("/proc"),
37 }
38 }
39}
40
41pub(crate) fn read_trimmed(path: &Path) -> Option<String> {
47 fs::read_to_string(path).ok().map(|s| s.trim().to_owned())
48}
49
50pub(crate) fn read_u64(path: &Path) -> Option<u64> {
52 read_trimmed(path)?.parse().ok()
53}
54
55pub(crate) fn read_i64(path: &Path) -> Option<i64> {
57 read_trimmed(path)?.parse().ok()
58}
59
60pub(crate) fn sorted_entries(dir: &Path) -> Vec<String> {
62 let mut names: Vec<String> = match fs::read_dir(dir) {
63 Ok(entries) => entries
64 .filter_map(|e| e.ok())
65 .filter_map(|e| e.file_name().into_string().ok())
66 .collect(),
67 Err(_) => Vec::new(),
68 };
69 names.sort_by(|a, b| natural_cmp(a, b));
70 names
71}
72
73pub(crate) fn natural_cmp(a: &str, b: &str) -> std::cmp::Ordering {
76 fn split(s: &str) -> (&str, Option<u64>) {
79 let digits_start = s
80 .char_indices()
81 .rev()
82 .take_while(|(_, c)| c.is_ascii_digit())
83 .last()
84 .map(|(i, _)| i)
85 .unwrap_or(s.len());
86 let (prefix, digits) = s.split_at(digits_start);
87 (prefix, digits.parse().ok())
88 }
89 let (pa, na) = split(a);
90 let (pb, nb) = split(b);
91 pa.cmp(pb).then(na.cmp(&nb)).then_with(|| a.cmp(b))
92}
93
94#[cfg(test)]
95pub(crate) mod testutil {
96 use std::fs;
99 use std::path::Path;
100
101 pub fn write(root: &Path, rel: &str, content: &str) {
103 let path = root.join(rel);
104 fs::create_dir_all(path.parent().unwrap()).unwrap();
105 fs::write(path, content).unwrap();
106 }
107
108 pub fn symlink(root: &Path, rel: &str, target: &Path) {
110 let path = root.join(rel);
111 fs::create_dir_all(path.parent().unwrap()).unwrap();
112 std::os::unix::fs::symlink(target, path).unwrap();
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::natural_cmp;
119
120 #[test]
121 fn natural_order_sorts_numbers_numerically() {
122 let mut names = vec!["hwmon10", "hwmon2", "hwmon1", "fan"];
123 names.sort_by(|a, b| natural_cmp(a, b));
124 assert_eq!(names, vec!["fan", "hwmon1", "hwmon2", "hwmon10"]);
125 }
126}