i3status_rs/blocks/
rofication.rs1use super::prelude::*;
34use tokio::net::UnixStream;
35
36#[derive(Deserialize, Debug, SmartDefault)]
37#[serde(deny_unknown_fields, default)]
38pub struct Config {
39 #[default(1.into())]
40 pub interval: Seconds,
41 #[default("/tmp/rofi_notification_daemon".into())]
42 pub socket_path: ShellString,
43 pub format: FormatConfig,
44}
45
46pub(crate) fn prepare(config: &Config) -> Result<Arc<BlockPlan>> {
47 BlockPlan::new(vec![
48 OutputPlan::new("main", config.format.with_default(" $icon $num.eng(w:1) ")?)
49 .icon("icon", IconChoices::one(icons::BELL)),
50 ])
51}
52
53pub(crate) async fn run(config: &Config, api: &CommonApi, plan: &Arc<BlockPlan>) -> Result<()> {
54 let output_main = plan.output("main")?;
55
56 let path = config.socket_path.expand()?;
57 let mut timer = config.interval.timer();
58
59 loop {
60 let (num, crit) = rofication_status(&path).await?;
61
62 let mut widget = output_main.new_widget();
63
64 widget.set_values(map!(
65 "icon" => Value::icon(icons::BELL),
66 "num" => Value::number(num)
67 ));
68
69 widget.state = if crit > 0 {
70 State::Warning
71 } else if num > 0 {
72 State::Info
73 } else {
74 State::Idle
75 };
76
77 api.set_widget(widget)?;
78
79 tokio::select! {
80 _ = timer.tick() => (),
81 _ = api.wait_for_update_request() => (),
82 }
83 }
84}
85
86async fn rofication_status(socket_path: &str) -> Result<(usize, usize)> {
87 let mut stream = UnixStream::connect(socket_path)
88 .await
89 .error("Failed to connect to socket")?;
90
91 stream
93 .write_all(b"num:\n")
94 .await
95 .error("Failed to write to socket")?;
96
97 let mut response = String::new();
98 stream
99 .read_to_string(&mut response)
100 .await
101 .error("Failed to read from socket")?;
102
103 let (num, crit) = response
105 .split_once([',', '\n'])
106 .error("Incorrect response")?;
107 Ok((
108 num.parse().error("Incorrect response")?,
109 crit.parse().error("Incorrect response")?,
110 ))
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn plan_declares_main_output_with_bell_icon() {
119 let plan = prepare(&Config::default()).unwrap();
120 let declared: Vec<_> = plan.outputs().map(|o| o.id()).collect();
121 assert_eq!(declared, ["main"]);
122 let output = plan.output("main").unwrap();
123 assert_eq!(output.single_icon("icon").unwrap(), "bell");
124 }
125
126 #[test]
127 fn custom_format_is_respected() {
128 let config = Config {
129 format: " $num ".parse().unwrap(),
130 ..Config::default()
131 };
132 let plan = prepare(&config).unwrap();
133 let output = plan.output("main").unwrap();
134 assert!(output.format().contains_key("num"));
135 assert!(!output.format().contains_key("icon"));
136 }
137}