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