i3status_rs/blocks/
uptime.rs1use super::prelude::*;
33use tokio::fs::read_to_string;
34
35#[derive(Deserialize, Debug, SmartDefault)]
36#[serde(deny_unknown_fields, default)]
37pub struct Config {
38 pub format: FormatConfig,
39 #[default(60.into())]
40 pub interval: Seconds,
41}
42
43pub(crate) fn prepare(config: &Config) -> Result<Arc<BlockPlan>> {
44 BlockPlan::new(vec![
45 OutputPlan::new("main", config.format.with_default(" $icon $uptime ")?)
46 .icon("icon", IconChoices::one(icons::UPTIME)),
47 ])
48}
49
50pub(crate) async fn run(config: &Config, api: &CommonApi, plan: &Arc<BlockPlan>) -> Result<()> {
51 let output_main = plan.output("main")?;
52
53 loop {
54 let uptime = read_to_string("/proc/uptime")
55 .await
56 .error("Failed to read /proc/uptime")?;
57 let mut seconds: u64 = uptime
58 .split('.')
59 .next()
60 .and_then(|u| u.parse().ok())
61 .error("/proc/uptime has invalid content")?;
62
63 let uptime = Duration::from_secs(seconds);
64
65 let weeks = seconds / 604_800;
66 seconds %= 604_800;
67 let days = seconds / 86_400;
68 seconds %= 86_400;
69 let hours = seconds / 3_600;
70 seconds %= 3_600;
71 let minutes = seconds / 60;
72 seconds %= 60;
73
74 let text = if weeks > 0 {
75 format!("{weeks}w {days}d")
76 } else if days > 0 {
77 format!("{days}d {hours}h")
78 } else if hours > 0 {
79 format!("{hours}h {minutes}m")
80 } else {
81 format!("{minutes}m {seconds}s")
82 };
83
84 let mut widget = output_main.new_widget();
85 widget.set_values(map! {
86 "icon" => Value::icon(icons::UPTIME),
87 "text" => Value::text(text),
88 "uptime" => Value::duration(uptime)
89 });
90 api.set_widget(widget)?;
91
92 select! {
93 _ = sleep(config.interval.0) => (),
94 _ = api.wait_for_update_request() => (),
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn plan_declares_main_output_with_uptime_icon() {
105 let plan = prepare(&Config::default()).unwrap();
106 let declared: Vec<_> = plan.outputs().map(|o| o.id()).collect();
107 assert_eq!(declared, ["main"]);
108 let output = plan.output("main").unwrap();
109 assert_eq!(output.single_icon("icon").unwrap(), "uptime");
110 }
111
112 #[test]
113 fn custom_format_is_respected() {
114 let config = Config {
115 format: " $uptime ".parse().unwrap(),
116 ..Config::default()
117 };
118 let plan = prepare(&config).unwrap();
119 let output = plan.output("main").unwrap();
120 assert!(output.format().contains_key("uptime"));
121 assert!(!output.format().contains_key("icon"));
122 }
123}