pub fn total_memory_mb() -> Option<u64> {
let content = std::fs::read_to_string("/proc/meminfo").ok()?;
for line in content.lines() {
let Some(rest) = line.strip_prefix("MemTotal:") else {
continue;
};
let kb = rest.trim().trim_end_matches("kB").trim().parse::<u64>().ok()?;
return Some(kb / 1024);
}
None
}
pub fn is_on_battery() -> bool {
let Ok(entries) = std::fs::read_dir("/sys/class/power_supply") else {
return false;
};
for entry in entries.flatten() {
let status_path = entry.path().join("status");
if let Ok(status) = std::fs::read_to_string(&status_path) {
if status.trim() == "Discharging" {
return true;
}
}
}
false
}
pub fn process_memory_utilization() -> Option<f32> {
let status = std::fs::read_to_string("/proc/self/status").ok()?;
let mut vmrss_kb: u64 = 0;
let mut vmsize_kb: u64 = 0;
for line in status.lines() {
if let Some(rest) = line.strip_prefix("VmRSS:") {
vmrss_kb = rest.trim().trim_end_matches("kB").trim().parse().unwrap_or(0);
} else if let Some(rest) = line.strip_prefix("VmSize:") {
vmsize_kb = rest.trim().trim_end_matches("kB").trim().parse().unwrap_or(0);
}
}
if vmsize_kb == 0 {
return None;
}
Some((vmrss_kb as f32 / vmsize_kb as f32).clamp(0.0, 1.0))
}
pub fn process_cpu_utilization() -> Option<f32> {
let status = std::fs::read_to_string("/proc/self/status").ok()?;
for line in status.lines() {
let Some(rest) = line.strip_prefix("Threads:") else {
continue;
};
let threads = rest.trim().parse::<f32>().ok()?;
let cores = std::thread::available_parallelism().map(|n| n.get() as f32).unwrap_or(4.0);
return Some((threads / (cores * 2.0)).clamp(0.0, 1.0));
}
None
}
pub fn spawn_print_job(job_file: &std::path::Path) -> Result<(), String> {
let attempts: [&str; 2] = ["lpr", "lp"];
let mut last_error = String::from("no print spooler found");
for program in attempts {
match std::process::Command::new(program).arg(job_file).spawn() {
Ok(_child) => return Ok(()),
Err(error) => {
last_error = format!("{program}: {error}");
}
}
}
Err(last_error)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn probes_are_total() {
if let Some(mb) = total_memory_mb() {
assert!(mb > 0, "a readable /proc/meminfo must report a positive size");
}
if let Some(ratio) = process_memory_utilization() {
assert!((0.0..=1.0).contains(&ratio), "RSS/VmSize must be a fraction, got {ratio}");
}
if let Some(ratio) = process_cpu_utilization() {
assert!((0.0..=1.0).contains(&ratio), "thread ratio must be clamped, got {ratio}");
}
let _ = is_on_battery();
}
#[test]
fn print_job_reports_failure_for_a_missing_spooler_or_file() {
let missing = std::path::Path::new("/nonexistent/rust_widgets_probe_job.pdf");
let result = spawn_print_job(missing);
let _ = result;
}
}