use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum Platform {
#[default]
Undetected,
Linux,
MacOS,
Other(String),
}
impl Platform {
pub fn from_uname(raw: &str) -> Self {
match raw.trim() {
"Linux" => Platform::Linux,
"Darwin" => Platform::MacOS,
"" => Platform::Undetected,
other => Platform::Other(other.to_string()),
}
}
pub fn label(&self) -> &str {
match self {
Platform::Undetected => "unknown",
Platform::Linux => "Linux",
Platform::MacOS => "macOS",
Platform::Other(s) => s,
}
}
pub fn is_supported(&self) -> bool {
matches!(self, Platform::Linux | Platform::MacOS)
}
pub fn metrics_command(&self, proc_limit: usize) -> Option<String> {
match self {
Platform::Linux => Some(linux_command()),
Platform::MacOS => Some(macos_command(proc_limit)),
_ => None,
}
}
}
fn linux_command() -> String {
concat!(
"echo '===UNAME==='; uname -s; ",
"echo '===CPUSTAT==='; cat /proc/stat; ",
"echo '===MEMINFO==='; cat /proc/meminfo; ",
"echo '===LOADAVG==='; cat /proc/loadavg; ",
"echo '===DF==='; df -Pk; ",
"echo '===NETDEV==='; cat /proc/net/dev; ",
"echo '===UPTIME==='; cat /proc/uptime; ",
"echo '===PS==='; ps aux --sort=-%cpu 2>/dev/null || ps aux; ",
"echo '===END==='"
)
.to_string()
}
fn macos_command(proc_limit: usize) -> String {
format!(
concat!(
"echo '===UNAME==='; uname -s; ",
"echo '===CPUSTAT==='; (iostat -c 2 -w 1 2>/dev/null || top -l 2 -n 0 -s 1 2>/dev/null); ",
"echo '===VMSTAT==='; vm_stat; ",
"echo '===MEMSIZE==='; sysctl -n hw.memsize; ",
"echo '===SWAP==='; sysctl -n vm.swapusage; ",
"echo '===LOADAVG==='; sysctl -n vm.loadavg; ",
"echo '===NCPU==='; sysctl -n hw.ncpu; ",
"echo '===DF==='; df -Pk; ",
"echo '===NETDEV==='; netstat -ib; ",
"echo '===BOOTTIME==='; sysctl -n kern.boottime; ",
"echo '===NOW==='; date +%s; ",
"echo '===PS==='; ps axo user,pid,pcpu,pmem,vsz,rss,tty,state,start,time,command -r 2>/dev/null | head -n {}; ",
"echo '===END==='"
),
proc_limit + 1 )
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn uname_maps_to_platforms() {
assert_eq!(Platform::from_uname("Linux\n"), Platform::Linux);
assert_eq!(Platform::from_uname("Darwin\n"), Platform::MacOS);
assert_eq!(Platform::from_uname(""), Platform::Undetected);
assert_eq!(
Platform::from_uname("FreeBSD\n"),
Platform::Other("FreeBSD".to_string())
);
}
#[test]
fn unsupported_platforms_have_no_command() {
assert!(Platform::Other("FreeBSD".into())
.metrics_command(6)
.is_none());
assert!(Platform::Undetected.metrics_command(6).is_none());
assert!(!Platform::Other("FreeBSD".into()).is_supported());
}
#[test]
fn both_platforms_pin_df_to_1k_blocks() {
for p in [Platform::Linux, Platform::MacOS] {
let cmd = p.metrics_command(6).unwrap();
assert!(cmd.contains("df -Pk"), "{} must pin df blocks", p.label());
}
}
#[test]
fn both_platforms_emit_the_same_envelope() {
for p in [Platform::Linux, Platform::MacOS] {
let cmd = p.metrics_command(6).unwrap();
for section in ["UNAME", "CPUSTAT", "LOADAVG", "DF", "NETDEV", "PS", "END"] {
assert!(
cmd.contains(&format!("==={}===", section)),
"{} missing {}",
p.label(),
section
);
}
}
}
}