1use std::fmt;
2use std::fs;
3
4use colorsys::Rgb;
5
6use crate::common::{parse_colour_param, pct_value_hsl};
7
8#[derive(Debug, PartialEq)]
9pub enum MemAppletError {
10 MemInfoUnavailable,
11}
12
13impl std::error::Error for MemAppletError {}
14
15impl fmt::Display for MemAppletError {
16 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
17 write!(f, "MemAppletError: {:?}", self)
18 }
19}
20
21type Result<T> = std::result::Result<T, MemAppletError>;
22
23#[derive(Debug, PartialEq)]
24pub struct MemInfo {
25 pub total: u32,
26 pub used: u32,
27 pub available: u32,
28}
29
30const MEM_INFO_PATH: &str = "/proc/meminfo";
31
32fn read_meminfo() -> Result<MemInfo> {
33 let data = fs::read_to_string(MEM_INFO_PATH).or(Err(MemAppletError::MemInfoUnavailable))?;
34
35 let mut info = MemInfo { available: 0, total: 0, used: 0 };
36
37 for line in data.lines() {
38 if info.total != 0 && info.available != 0 {
39 break;
40 }
41
42 let mut parts = line.split_whitespace();
43 let Some(key) = parts.next() else { continue };
44 let Some(value) = parts.next() else { continue };
45
46 match key {
47 "MemTotal:" => {
48 info.total = value.parse::<u32>().unwrap_or(0);
49 }
50 "MemAvailable:" => {
51 info.available = value.parse::<u32>().unwrap_or(0);
52 }
53 _ => continue,
54 }
55 }
56
57 if info.available > 0 && info.total > 0 {
58 info.used = info.total - info.available
59 }
60
61 Ok(info)
62}
63
64fn normalise_mem_usage(info: &MemInfo) -> f32 {
65 info.used as f32 / info.total as f32
66}
67
68pub fn applet(args: &[String]) -> Result<()> {
69 let mut colour_s: Option<f32> = None;
70 let mut colour_l: Option<f32> = None;
71 let mut show_pct_text: bool = false;
72
73 for arg in args {
74 if arg == "pct-text" {
75 show_pct_text = true;
76 continue;
77 }
78 if let Some(s) = parse_colour_param(arg, "s") {
79 if (0.0..=100.0).contains(&s) {
80 colour_s = Some(s);
81 } else {
82 eprintln!("Saturation {s} out of range [0, 100.0]");
83 }
84 };
85 if let Some(l) = parse_colour_param(arg, "l") {
86 if (0.0..=100.0).contains(&l) {
87 colour_l = Some(l);
88 } else {
89 eprintln!("Lightness {l} out of range [0, 100.0]");
90 }
91 }
92 }
93
94 let info = read_meminfo()?;
95 let norm = normalise_mem_usage(&info);
96
97 eprintln!("Mem Info: {:?} Pct: {:.2}", info, norm);
98
99 let c = pct_value_hsl(norm, colour_s, colour_l);
100 let rgb = Rgb::from(&c);
101
102 if show_pct_text {
103 let val = format!("{:.0}", ((norm * 100.0).round()));
104 print!("#[bg={}]{:>2}", rgb.to_hex_string(), val);
105 } else {
106 print!("#[bg={}] ", rgb.to_hex_string());
107 }
108 println!("#[default]");
109
110 Ok(())
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn test_read_mem_info() {
119 let m = read_meminfo();
120 assert_eq!(true, m.is_ok());
121 let m = m.unwrap();
122 assert_ne!(0, m.total);
123 assert_ne!(0, m.available);
124 assert_ne!(0, m.used);
125 println!("mem_info: {:?}", m);
126 }
127}