rs_blocks/blocks/
memory.rs

1// Copyright ⓒ 2019-2021 Lewis Belcher
2// Licensed under the MIT license (see LICENSE or <http://opensource.org/licenses/MIT>).
3// All files in the project carrying such notice may not be copied, modified, or
4// distributed except according to those terms
5
6//! # Memory block
7//!
8//! Use this block to get memory monitoring in the status bar. Reads from
9//! `/proc/meminfo` to calculate memory usage.
10//!
11//! Typical configuration:
12//!
13//! ```toml
14//! [memory]
15//! ```
16//!
17//! ## Configuration options
18//!
19//! - `name`: Name of the block (must be unique)
20//! - `period`: Default update period in seconds (extra updates may occur on
21//!    event changes etc)
22//! - `alpha`: Weight for the exponential moving average of value updates
23
24use crate::blocks::{Block, Configure, Message, Sender};
25use crate::{ema, utils};
26use regex::Regex;
27use serde::Deserialize;
28use std::thread;
29
30const MEMPATH: &str = "/proc/meminfo";
31const PATTERN: &str = r"(?s)MemTotal:\s+(\d+).+MemFree:\s+(\d+)";
32
33#[derive(Configure, Deserialize)]
34pub struct Memory {
35	#[serde(default = "default_name")]
36	name: String,
37	#[serde(default = "default_period")]
38	period: f32,
39	#[serde(default = "default_alpha")]
40	alpha: f32,
41}
42
43fn default_name() -> String {
44	"memory".to_string()
45}
46
47fn default_period() -> f32 {
48	1.0
49}
50
51fn default_alpha() -> f32 {
52	0.5
53}
54
55impl Sender for Memory {
56	fn add_sender(&self, channel: crossbeam_channel::Sender<Message>) -> anyhow::Result<()> {
57		let name = self.get_name();
58		let monitor = utils::monitor_file(MEMPATH.to_string(), self.period);
59		let mut mem = ema::Ema::new(self.alpha);
60		let mut block = Block::new(name.clone(), true);
61
62		thread::spawn(move || {
63			for text in monitor {
64				let perc = get_mem_percentage(match_mem_stats(&text));
65				block.full_text = Some(format!(" {:.1}%", mem.push(perc) * 100.0));
66				channel.send((name.clone(), block.to_string())).unwrap();
67			}
68		});
69
70		Ok(())
71	}
72}
73
74#[derive(Debug, PartialEq)]
75struct MemStats {
76	total: f64,
77	free: f64,
78}
79
80fn match_mem_stats(s: &str) -> MemStats {
81	lazy_static! {
82		static ref RE: Regex = Regex::new(PATTERN).unwrap();
83	}
84	let caps = RE.captures(s).unwrap();
85	MemStats {
86		total: caps.get(1).unwrap().as_str().parse().unwrap(),
87		free: caps.get(2).unwrap().as_str().parse().unwrap(),
88	}
89}
90
91fn get_mem_percentage(mem: MemStats) -> f32 {
92	1.0 - mem.free as f32 / mem.total as f32
93}
94
95#[cfg(test)]
96mod tests {
97	use super::*;
98	const MEMFILE: &str = "MemTotal:  16134372 kB\nMemFree:  2757408 kB\n";
99
100	#[test]
101	fn regex_matches() {
102		let mem = match_mem_stats(&MEMFILE);
103		assert_eq!(
104			mem,
105			MemStats {
106				total: 16134372.0,
107				free: 2757408.0
108			}
109		);
110	}
111}