use crate::detect::{Row, Rows};
use crate::util::{human_iec, percent};
pub fn detect() -> Rows {
let Ok(info) = std::fs::read_to_string("/proc/meminfo") else {
return Vec::new();
};
let mut total = 0u64;
let mut available = 0u64;
for line in info.lines() {
if let Some(v) = line.strip_prefix("MemTotal:") {
total = parse_kb(v);
} else if let Some(v) = line.strip_prefix("MemAvailable:") {
available = parse_kb(v);
}
}
if total == 0 {
return Vec::new();
}
let mut used = total.saturating_sub(available);
if let Some(reclaimable) = zfs_arc_reclaimable() {
used = used.saturating_sub(reclaimable);
}
vec![Row::val(format!(
"{} / {} ({}%)",
human_iec(used),
human_iec(total),
percent(used, total)
))]
}
fn zfs_arc_reclaimable() -> Option<u64> {
let stats = std::fs::read_to_string("/proc/spl/kstat/zfs/arcstats").ok()?;
let mut size = 0u64;
let mut c_min = 0u64;
for line in stats.lines() {
let mut fields = line.split_whitespace();
match fields.next() {
Some("size") => size = fields.nth(1).and_then(|d| d.parse().ok()).unwrap_or(0),
Some("c_min") => c_min = fields.nth(1).and_then(|d| d.parse().ok()).unwrap_or(0),
_ => {}
}
}
(size > c_min).then_some(size - c_min)
}
fn parse_kb(s: &str) -> u64 {
s.split_whitespace()
.next()
.and_then(|n| n.parse::<u64>().ok())
.map(|kb| kb * 1024)
.unwrap_or(0)
}