//! Data collection for memory via heim. #[derive(Debug, Clone, Default)] pub struct MemHarvest { pub mem_total_in_kib: u64, pub mem_used_in_kib: u64, pub use_percent: Option, } pub async fn get_mem_data( actually_get: bool, ) -> ( crate::utils::error::Result>, crate::utils::error::Result>, ) { use futures::join; if !actually_get { (Ok(None), Ok(None)) } else { join!(get_ram_data(), get_swap_data()) } } pub async fn get_ram_data() -> crate::utils::error::Result> { let (mem_total_in_kib, mem_used_in_kib) = { #[cfg(target_os = "linux")] { let mem_info = procfs::Meminfo::new()?; // Let's preface this by saying that memory usage calculations are... not straightforward. // There are conflicting implementations everywhere. // // Now that we've added this preface (mainly for future reference), the current implementation below for usage // is based on htop's calculation formula. See // https://github.com/htop-dev/htop/blob/976c6123f41492aaf613b9d172eef1842fb7b0a3/linux/LinuxProcessList.c#L1584 // for implementation details as of writing. // // Another implementation, commonly used in other things, is to skip the shmem part of the calculation, // which matches gopsutil and stuff like free. let total = mem_info.mem_total / 1024; let cached_mem = mem_info.cached + mem_info.s_reclaimable.unwrap_or(0) - mem_info.shmem.unwrap_or(0); let used_diff = (mem_info.mem_free + cached_mem + mem_info.buffers) / 1024; let used = if total >= used_diff { total - used_diff } else { total - mem_info.mem_free }; (total, used) } #[cfg(target_os = "macos")] { let memory = heim::memory::memory().await?; use heim::memory::os::macos::MemoryExt; use heim::units::information::kibibyte; ( memory.total().get::(), memory.active().get::() + memory.wire().get::(), ) } #[cfg(target_os = "windows")] { let memory = heim::memory::memory().await?; use heim::units::information::kibibyte; let mem_total_in_kib = memory.total().get::(); ( mem_total_in_kib, mem_total_in_kib - memory.available().get::(), ) } }; Ok(Some(MemHarvest { mem_total_in_kib, mem_used_in_kib, use_percent: if mem_total_in_kib == 0 { None } else { Some(mem_used_in_kib as f64 / mem_total_in_kib as f64 * 100.0) }, })) } pub async fn get_swap_data() -> crate::utils::error::Result> { let memory = heim::memory::swap().await?; let (mem_total_in_kib, mem_used_in_kib) = { #[cfg(target_os = "linux")] { // Similar story to above - heim parses this information incorrectly as far as I can tell, so kilobytes = kibibytes here. use heim::units::information::kilobyte; ( memory.total().get::(), memory.used().get::(), ) } #[cfg(any(target_os = "windows", target_os = "macos"))] { use heim::units::information::kibibyte; ( memory.total().get::(), memory.used().get::(), ) } }; Ok(Some(MemHarvest { mem_total_in_kib, mem_used_in_kib, use_percent: if mem_total_in_kib == 0 { None } else { Some(mem_used_in_kib as f64 / mem_total_in_kib as f64 * 100.0) }, })) }