Skip to main content

i3status_rs/blocks/
load.rs

1//! System load average
2//!
3//! # Configuration
4//!
5//! Key        | Values                                                                                | Default
6//! -----------|---------------------------------------------------------------------------------------|--------
7//! `format`   | A string to customise the output of this block. See below for available placeholders. | `" $icon $1m.eng(w:4) "`
8//! `interval` | Update interval in seconds                                                            | `3`
9//! `info`     | Minimum load, where state is set to info                                              | `0.3`
10//! `warning`  | Minimum load, where state is set to warning                                           | `0.6`
11//! `critical` | Minimum load, where state is set to critical                                          | `0.9`
12//!
13//! Placeholder  | Value                  | Type   | Unit
14//! -------------|------------------------|--------|-----
15//! `icon`       | A static icon          | Icon   | -
16//! `1m`         | 1 minute load average  | Number | -
17//! `5m`         | 5 minute load average  | Number | -
18//! `15m`        | 15 minute load average | Number | -
19//!
20//! # Example
21//!
22//! ```toml
23//! [[block]]
24//! block = "load"
25//! format = " $icon 1min avg: $1m.eng(w:4) "
26//! interval = 1
27//! ```
28//!
29//! # Icons Used
30//! - `cogs` (`$icon`)
31
32use super::prelude::*;
33use crate::util;
34
35#[derive(Deserialize, Debug, SmartDefault)]
36#[serde(deny_unknown_fields, default)]
37pub struct Config {
38    pub format: FormatConfig,
39    #[default(3.into())]
40    pub interval: Seconds,
41    #[default(0.3)]
42    pub info: f64,
43    #[default(0.6)]
44    pub warning: f64,
45    #[default(0.9)]
46    pub critical: f64,
47}
48
49pub(crate) fn prepare(config: &Config) -> Result<Arc<BlockPlan>> {
50    BlockPlan::new(vec![
51        OutputPlan::new("main", config.format.with_default(" $icon $1m.eng(w:4) ")?)
52            .icon("icon", IconChoices::one(icons::COGS)),
53    ])
54}
55
56pub(crate) async fn run(config: &Config, api: &CommonApi, plan: &Arc<BlockPlan>) -> Result<()> {
57    let output_main = plan.output("main")?;
58
59    // borrowed from https://docs.rs/cpuinfo/0.1.1/src/cpuinfo/count/logical.rs.html#4-6
60    let logical_cores = util::read_file("/proc/cpuinfo")
61        .await
62        .error("Your system doesn't support /proc/cpuinfo")?
63        .lines()
64        .filter(|l| l.starts_with("processor"))
65        .count();
66
67    loop {
68        let loadavg = util::read_file("/proc/loadavg")
69            .await
70            .error("Your system does not support reading the load average from /proc/loadavg")?;
71        let mut values = loadavg.split(' ');
72        let m1: f64 = values
73            .next()
74            .and_then(|x| x.parse().ok())
75            .error("bad /proc/loadavg file")?;
76        let m5: f64 = values
77            .next()
78            .and_then(|x| x.parse().ok())
79            .error("bad /proc/loadavg file")?;
80        let m15: f64 = values
81            .next()
82            .and_then(|x| x.parse().ok())
83            .error("bad /proc/loadavg file")?;
84
85        let mut widget = output_main.new_widget();
86        widget.state = match m1 / logical_cores as f64 {
87            x if x > config.critical => State::Critical,
88            x if x > config.warning => State::Warning,
89            x if x > config.info => State::Info,
90            _ => State::Idle,
91        };
92        widget.set_values(map! {
93            "icon" => Value::icon(icons::COGS),
94            "1m" => Value::number(m1),
95            "5m" => Value::number(m5),
96            "15m" => Value::number(m15),
97        });
98        api.set_widget(widget)?;
99
100        select! {
101            _ = sleep(config.interval.0) => (),
102            _ = api.wait_for_update_request() => (),
103        }
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn plan_declares_main_output_with_cogs_icon() {
113        let plan = prepare(&Config::default()).unwrap();
114        let declared: Vec<_> = plan.outputs().map(|o| o.id()).collect();
115        assert_eq!(declared, ["main"]);
116        let output = plan.output("main").unwrap();
117        assert_eq!(output.single_icon("icon").unwrap(), "cogs");
118    }
119
120    #[test]
121    fn custom_format_is_respected() {
122        let config = Config {
123            format: " $5m ".parse().unwrap(),
124            ..Config::default()
125        };
126        let plan = prepare(&config).unwrap();
127        let output = plan.output("main").unwrap();
128        assert!(output.format().contains_key("5m"));
129        assert!(!output.format().contains_key("icon"));
130    }
131}