1use crate::unit::DataSize;
12use crate::util::read_to_string_mut;
13
14use std::path::Path;
15use std::{fs, io};
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct Memory {
20 raw: String,
21}
22
23impl Memory {
24 fn path() -> &'static Path {
25 Path::new("/proc/meminfo")
26 }
27
28 #[cfg(test)]
29 fn from_string(raw: String) -> Self {
30 Self { raw }
31 }
32
33 pub fn read() -> io::Result<Self> {
35 Ok(Self {
36 raw: fs::read_to_string(Self::path())?,
37 })
38 }
39
40 pub fn reload(&mut self) -> io::Result<()> {
42 read_to_string_mut(Self::path(), &mut self.raw)
43 }
44
45 pub fn values<'a>(&'a self) -> impl Iterator<Item = (&'a str, &'a str)> {
47 self.raw.split('\n').filter_map(|line| {
48 let mut iter = line.splitn(2, ':');
50 let (key, value) = (iter.next()?, iter.next()?);
51 Some((key.trim(), value.trim()))
52 })
53 }
54
55 pub fn keys<'a>(&'a self) -> impl Iterator<Item = &'a str> {
57 self.values().map(|(k, _)| k)
58 }
59
60 pub fn value<'a>(&'a self, key: &str) -> Option<&'a str> {
62 self.values()
63 .find_map(|(k, v)| k.eq_ignore_ascii_case(key).then(|| v))
64 }
65
66 pub fn size_value<'a>(&'a self, key: &str) -> Option<DataSize> {
68 self.value(key).and_then(DataSize::from_str)
69 }
70
71 pub fn total_memory(&self) -> Option<DataSize> {
73 self.size_value("MemTotal")
74 }
75
76 pub fn free_memory(&self) -> Option<DataSize> {
78 self.size_value("MemFree")
79 }
80
81 pub fn available_memory(&self) -> Option<DataSize> {
83 self.size_value("MemAvailable")
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use crate::unit::DataSizeUnit;
91
92 fn memory_info() -> Memory {
93 Memory::from_string(
94 "\
95MemTotal: 32853280 kB
96MemFree: 919776 kB
97MemAvailable: 28781828 kB
98Buffers: 298460 kB
99Cached: 27104800 kB
100SwapCached: 168 kB
101Active: 7764012 kB
102Inactive: 22289624 kB
103Active(anon): 2257064 kB
104Inactive(anon): 624500 kB
105Active(file): 5506948 kB
106Inactive(file): 21665124 kB
107Unevictable: 16 kB
108Mlocked: 16 kB
109SwapTotal: 2097148 kB
110SwapFree: 2094844 kB
111Dirty: 360 kB
112Writeback: 0 kB
113AnonPages: 2650504 kB
114Mapped: 760008 kB
115Shmem: 231188 kB
116KReclaimable: 1154740 kB
117Slab: 1529684 kB
118SReclaimable: 1154740 kB
119SUnreclaim: 374944 kB
120KernelStack: 21600 kB
121PageTables: 31948 kB
122NFS_Unstable: 0 kB
123Bounce: 0 kB
124WritebackTmp: 0 kB
125CommitLimit: 18523788 kB
126Committed_AS: 9191380 kB
127VmallocTotal: 34359738367 kB
128VmallocUsed: 62668 kB
129VmallocChunk: 0 kB
130Percpu: 37376 kB
131HardwareCorrupted: 0 kB
132AnonHugePages: 0 kB
133ShmemHugePages: 0 kB
134ShmemPmdMapped: 0 kB
135FileHugePages: 0 kB
136FilePmdMapped: 0 kB
137HugePages_Total: 0
138HugePages_Free: 0
139HugePages_Rsvd: 0
140HugePages_Surp: 0
141Hugepagesize: 2048 kB
142Hugetlb: 0 kB
143DirectMap4k: 922564 kB
144DirectMap2M: 10518528 kB
145DirectMap1G: 22020096 kB\
146 "
147 .into(),
148 )
149 }
150
151 #[test]
152 fn total_memory() {
153 let mem_info = memory_info();
154 let total_memory = mem_info.total_memory().unwrap();
155 assert_eq!(total_memory.to(&DataSizeUnit::Kb), 32853280.0);
156 }
157}