const DEFAULT_RAM_GB: f64 = 8.0;
const RAM_HEADROOM_FACTOR: f64 = 0.8;
#[must_use]
pub fn get_total_ram_gb() -> f64 {
get_total_ram_bytes()
.map(|bytes| bytes as f64 / 1_000_000_000.0)
.unwrap_or(DEFAULT_RAM_GB)
}
#[must_use]
pub fn get_total_ram_bytes() -> Option<u64> {
#[cfg(target_os = "macos")]
{
use std::process::Command;
Command::new("sysctl")
.args(["-n", "hw.memsize"])
.output()
.ok()
.and_then(|output| String::from_utf8(output.stdout).ok())
.and_then(|s| s.trim().parse::<u64>().ok())
}
#[cfg(target_os = "linux")]
{
std::fs::read_to_string("/proc/meminfo")
.ok()
.and_then(|content| {
content
.lines()
.find(|line| line.starts_with("MemTotal:"))
.and_then(|line| {
line.split_whitespace()
.nth(1)
.and_then(|kb| kb.parse::<u64>().ok())
})
})
.map(|kb| kb * 1024)
}
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
{
None
}
}
#[must_use]
pub fn get_available_ram_gb() -> u32 {
(get_total_ram_gb() * RAM_HEADROOM_FACTOR) as u32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_total_ram_gb() {
let ram = get_total_ram_gb();
assert!(ram > 1.0, "RAM should be > 1 GB, got {}", ram);
assert!(ram < 1024.0, "RAM should be < 1 TB, got {}", ram);
}
#[test]
fn test_get_total_ram_bytes() {
let bytes = get_total_ram_bytes();
#[cfg(any(target_os = "macos", target_os = "linux"))]
{
assert!(bytes.is_some(), "RAM detection should succeed");
let bytes = bytes.unwrap();
assert!(bytes > 1_000_000_000, "RAM should be > 1 GB in bytes");
}
}
#[test]
fn test_get_available_ram_gb() {
let available = get_available_ram_gb();
let total = get_total_ram_gb() as u32;
assert!(
available <= total,
"Available {} should be <= total {}",
available,
total
);
assert!(available >= 1, "Available RAM should be >= 1 GB");
}
#[test]
fn test_default_ram_constant() {
assert_eq!(DEFAULT_RAM_GB, 8.0);
}
#[test]
fn test_headroom_factor() {
assert!((RAM_HEADROOM_FACTOR - 0.8).abs() < f64::EPSILON);
}
}