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