i3status_rs/blocks/
docker.rs1use super::prelude::*;
34
35#[derive(Deserialize, Debug, SmartDefault)]
36#[serde(deny_unknown_fields, default)]
37pub struct Config {
38 #[default(5.into())]
39 pub interval: Seconds,
40 pub format: FormatConfig,
41 #[default("/var/run/docker.sock".into())]
42 pub socket_path: ShellString,
43}
44
45pub(crate) fn prepare(config: &Config) -> Result<Arc<BlockPlan>> {
46 BlockPlan::new(vec![
47 OutputPlan::new(
48 "main",
49 config.format.with_default(" $icon $running.eng(w:1) ")?,
50 )
51 .icon("icon", IconChoices::one(icons::DOCKER)),
52 ])
53}
54
55pub(crate) async fn run(config: &Config, api: &CommonApi, plan: &Arc<BlockPlan>) -> Result<()> {
56 let output_main = plan.output("main")?;
57 let socket_path = config.socket_path.expand()?;
58
59 let client = reqwest::Client::builder()
60 .unix_socket(&*socket_path)
61 .build()
62 .unwrap();
63
64 loop {
65 let status: Status = client
66 .get("http://api/info")
67 .send()
68 .await
69 .error("Failed to get response")?
70 .json()
71 .await
72 .error("Failed to deserialize JSON")?;
73
74 let mut widget = output_main.new_widget();
75 widget.set_values(map! {
76 "icon" => Value::icon(icons::DOCKER),
77 "total" => Value::number(status.total),
78 "running" => Value::number(status.running),
79 "paused" => Value::number(status.paused),
80 "stopped" => Value::number(status.stopped),
81 "images" => Value::number(status.images),
82 });
83 api.set_widget(widget)?;
84
85 select! {
86 _ = sleep(config.interval.0) => (),
87 _ = api.wait_for_update_request() => (),
88 }
89 }
90}
91
92#[derive(Deserialize, Debug)]
93struct Status {
94 #[serde(rename = "Containers")]
95 total: i64,
96 #[serde(rename = "ContainersRunning")]
97 running: i64,
98 #[serde(rename = "ContainersStopped")]
99 stopped: i64,
100 #[serde(rename = "ContainersPaused")]
101 paused: i64,
102 #[serde(rename = "Images")]
103 images: i64,
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn plan_declares_main_output_with_docker_icon() {
112 let plan = prepare(&Config::default()).unwrap();
113 let declared: Vec<_> = plan.outputs().map(|o| o.id()).collect();
114 assert_eq!(declared, ["main"]);
115 let output = plan.output("main").unwrap();
116 assert_eq!(output.single_icon("icon").unwrap(), "docker");
117 }
118
119 #[test]
120 fn custom_format_is_respected() {
121 let config = Config {
122 format: " $running ".parse().unwrap(),
123 ..Config::default()
124 };
125 let plan = prepare(&config).unwrap();
126 let output = plan.output("main").unwrap();
127 assert!(output.format().contains_key("running"));
128 assert!(!output.format().contains_key("icon"));
129 }
130}