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 procstat;
23pub mod rapl;
24pub mod reboot;
25pub mod systemd;
26pub mod zfs;
27
28use std::fs;
29use std::path::{Path, PathBuf};
30
31#[derive(Debug, Clone)]
33pub struct SysRoots {
34 pub sys: PathBuf,
36 pub proc: PathBuf,
38}
39
40impl Default for SysRoots {
41 fn default() -> Self {
42 Self {
43 sys: PathBuf::from("/sys"),
44 proc: PathBuf::from("/proc"),
45 }
46 }
47}
48
49pub(crate) fn read_trimmed(path: &Path) -> Option<String> {
55 fs::read_to_string(path).ok().map(|s| s.trim().to_owned())
56}
57
58pub(crate) fn read_u64(path: &Path) -> Option<u64> {
60 read_trimmed(path)?.parse().ok()
61}
62
63pub(crate) fn read_i64(path: &Path) -> Option<i64> {
65 read_trimmed(path)?.parse().ok()
66}
67
68pub(crate) fn sorted_entries(dir: &Path) -> Vec<String> {
70 let mut names: Vec<String> = match fs::read_dir(dir) {
71 Ok(entries) => entries
72 .filter_map(|e| e.ok())
73 .filter_map(|e| e.file_name().into_string().ok())
74 .collect(),
75 Err(_) => Vec::new(),
76 };
77 names.sort_by(|a, b| natural_cmp(a, b));
78 names
79}
80
81pub(crate) fn natural_cmp(a: &str, b: &str) -> std::cmp::Ordering {
84 fn split(s: &str) -> (&str, Option<u64>) {
87 let digits_start = s
88 .char_indices()
89 .rev()
90 .take_while(|(_, c)| c.is_ascii_digit())
91 .last()
92 .map(|(i, _)| i)
93 .unwrap_or(s.len());
94 let (prefix, digits) = s.split_at(digits_start);
95 (prefix, digits.parse().ok())
96 }
97 let (pa, na) = split(a);
98 let (pb, nb) = split(b);
99 pa.cmp(pb).then(na.cmp(&nb)).then_with(|| a.cmp(b))
100}
101
102#[cfg(test)]
103pub(crate) mod testutil {
104 use std::fs;
107 use std::path::Path;
108
109 pub fn write(root: &Path, rel: &str, content: &str) {
111 let path = root.join(rel);
112 fs::create_dir_all(path.parent().unwrap()).unwrap();
113 fs::write(path, content).unwrap();
114 }
115
116 pub fn symlink(root: &Path, rel: &str, target: &Path) {
118 let path = root.join(rel);
119 fs::create_dir_all(path.parent().unwrap()).unwrap();
120 std::os::unix::fs::symlink(target, path).unwrap();
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::natural_cmp;
127
128 #[test]
129 fn natural_order_sorts_numbers_numerically() {
130 let mut names = vec!["hwmon10", "hwmon2", "hwmon1", "fan"];
131 names.sort_by(|a, b| natural_cmp(a, b));
132 assert_eq!(names, vec!["fan", "hwmon1", "hwmon2", "hwmon10"]);
133 }
134}