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