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