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