use sysinfo::{Pid, ProcessRefreshKind, ProcessesToUpdate, RefreshKind, System};
pub struct SysMetrics {
sys: System,
pid: Pid,
}
impl SysMetrics {
#[must_use]
pub fn new() -> Self {
let pid = Pid::from_u32(std::process::id());
let mut sys = System::new_with_specifics(
RefreshKind::nothing()
.with_processes(ProcessRefreshKind::nothing().with_memory().with_cpu()),
);
sys.refresh_processes_specifics(
ProcessesToUpdate::Some(&[pid]),
true,
ProcessRefreshKind::nothing().with_memory().with_cpu(),
);
Self { sys, pid }
}
pub fn sample(&mut self) -> (u64, f32) {
self.sys.refresh_processes_specifics(
ProcessesToUpdate::Some(&[self.pid]),
true,
ProcessRefreshKind::nothing().with_memory().with_cpu(),
);
match self.sys.process(self.pid) {
Some(proc) => (proc.memory() / (1024 * 1024), proc.cpu_usage()),
None => (0, 0.0),
}
}
}
impl Default for SysMetrics {
fn default() -> Self {
Self::new()
}
}
#[must_use]
pub fn dir_size_bytes(dir: &std::path::Path) -> u64 {
fn walk(dir: &std::path::Path, total: &mut u64) {
let Ok(entries) = std::fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let Ok(file_type) = entry.file_type() else {
continue;
};
if file_type.is_symlink() {
continue;
}
if file_type.is_dir() {
walk(&entry.path(), total);
continue;
}
if !file_type.is_file() {
continue;
}
if let Ok(meta) = entry.metadata() {
*total = total.saturating_add(meta.len());
}
}
}
let mut total = 0u64;
walk(dir, &mut total);
total
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sample_does_not_panic() {
let mut m = SysMetrics::new();
let (_rss, _cpu) = m.sample();
let (_rss2, cpu2) = m.sample();
assert!(cpu2 >= 0.0, "cpu usage must be non-negative, got {cpu2}");
}
#[test]
fn rss_is_plausible() {
let mut m = SysMetrics::new();
let (rss, _cpu) = m.sample();
assert!(
rss < 1024 * 1024,
"RSS implausibly large ({rss} MB) — unit must be MB"
);
}
#[test]
fn dir_size_sums_files() {
let tmp = tempfile::tempdir().expect("tempdir");
std::fs::write(tmp.path().join("a.txt"), vec![0u8; 100]).unwrap();
std::fs::write(tmp.path().join("b.txt"), vec![0u8; 250]).unwrap();
let sub = tmp.path().join("sub");
std::fs::create_dir(&sub).unwrap();
std::fs::write(sub.join("c.txt"), vec![0u8; 50]).unwrap();
assert_eq!(dir_size_bytes(tmp.path()), 400);
}
#[test]
fn dir_size_missing_dir_is_zero() {
let missing = std::path::Path::new("/nonexistent/trusty/path/xyz");
assert_eq!(dir_size_bytes(missing), 0);
}
}