Skip to content

Commit 42e25f5

Browse files
fix(ipc): support querying against duplicate bar names
It is possible/valid to define multiple bars by the same name by setting `name` on the top-level bar object, but not specifying monitors. This updates IPC to support this scenario. Allow IPC to act on multiple bars by the same name (#777)
1 parent e7c56ee commit 42e25f5

File tree

5 files changed

+75
-45
lines changed

5 files changed

+75
-45
lines changed

Diff for: docs/Controlling Ironbar.md

+17-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ You can also view help per sub-command or command, for example using `ironbar va
1010
The CLI supports plaintext and JSON output. Plaintext will:
1111

1212
- Print `ok` for empty success responses
13-
- Print the returned body for success responses
13+
- Print the returned body for each success response
14+
- Some commands act on multiple objects, in which case the CLI will print one line for each body.
1415
- Print `error` to followed by the error on the next line for error responses. This is printed to `stderr`.
1516

1617
Example:
@@ -150,6 +151,9 @@ Each key/value pair is on its own `\n` separated newline. The key and value are
150151

151152
### `bar`
152153

154+
> [!NOTE]
155+
> If there are multiple bars by the same name, the `bar` subcommand will act on all of them and return a `multi` response for commands that get a value.
156+
153157
#### `show`
154158

155159
Forces a bar to be shown, regardless of the current visibility state.
@@ -324,6 +328,17 @@ The operation completed successfully, with response data.
324328
}
325329
```
326330

331+
### `multi`
332+
333+
The operation completed successfully on multiple objects, with response data.
334+
335+
```json
336+
{
337+
"type": "multi",
338+
"values": ["lorem ipsum", "dolor sit"]
339+
}
340+
```
341+
327342
### `error`
328343

329344
The operation failed.
@@ -335,4 +350,4 @@ Message is optional.
335350
"type": "error",
336351
"message": "lorem ipsum"
337352
}
338-
```
353+
```

Diff for: src/cli.rs

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub fn handle_response(response: Response, format: Format) {
4646
Format::Plain => match response {
4747
Response::Ok => println!("ok"),
4848
Response::OkValue { value } => println!("{value}"),
49+
Response::Multi { values } => println!("{}", values.join("\n")),
4950
Response::Err { message } => eprintln!("error\n{}", message.unwrap_or_default()),
5051
},
5152
Format::Json => println!(

Diff for: src/ipc/responses.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize};
55
pub enum Response {
66
Ok,
77
OkValue { value: String },
8+
Multi { values: Vec<String> },
89
Err { message: Option<String> },
910
}
1011

Diff for: src/ipc/server/bar.rs

+51-39
Original file line numberDiff line numberDiff line change
@@ -8,48 +8,60 @@ use std::rc::Rc;
88
pub fn handle_command(command: BarCommand, ironbar: &Rc<Ironbar>) -> Response {
99
use BarCommandType::*;
1010

11-
let bar = ironbar.bar_by_name(&command.name);
12-
let Some(bar) = bar else {
13-
return Response::error("Invalid bar name");
14-
};
11+
let bars = ironbar.bars_by_name(&command.name);
1512

16-
match command.subcommand {
17-
Show => set_visible(&bar, true),
18-
Hide => set_visible(&bar, false),
19-
SetVisible { visible } => set_visible(&bar, visible),
20-
ToggleVisible => set_visible(&bar, !bar.visible()),
21-
GetVisible => Response::OkValue {
22-
value: bar.visible().to_string(),
23-
},
24-
25-
ShowPopup { widget_name } => show_popup(&bar, &widget_name),
26-
HidePopup => hide_popup(&bar),
27-
SetPopupVisible {
28-
widget_name,
29-
visible,
30-
} => {
31-
if visible {
32-
show_popup(&bar, &widget_name)
33-
} else {
34-
hide_popup(&bar)
13+
bars.into_iter()
14+
.map(|bar| match &command.subcommand {
15+
Show => set_visible(&bar, true),
16+
Hide => set_visible(&bar, false),
17+
SetVisible { visible } => set_visible(&bar, *visible),
18+
ToggleVisible => set_visible(&bar, !bar.visible()),
19+
GetVisible => Response::OkValue {
20+
value: bar.visible().to_string(),
21+
},
22+
ShowPopup { widget_name } => show_popup(&bar, widget_name),
23+
HidePopup => hide_popup(&bar),
24+
SetPopupVisible {
25+
widget_name,
26+
visible,
27+
} => {
28+
if *visible {
29+
show_popup(&bar, widget_name)
30+
} else {
31+
hide_popup(&bar)
32+
};
33+
Response::Ok
3534
}
36-
}
37-
TogglePopup { widget_name } => {
38-
if bar.popup().visible() {
39-
hide_popup(&bar)
40-
} else {
41-
show_popup(&bar, &widget_name)
35+
TogglePopup { widget_name } => {
36+
if bar.popup().visible() {
37+
hide_popup(&bar)
38+
} else {
39+
show_popup(&bar, widget_name)
40+
};
41+
Response::Ok
4242
}
43-
}
44-
GetPopupVisible => Response::OkValue {
45-
value: bar.popup().visible().to_string(),
46-
},
47-
SetExclusive { exclusive } => {
48-
bar.set_exclusive(exclusive);
49-
50-
Response::Ok
51-
}
52-
}
43+
GetPopupVisible => Response::OkValue {
44+
value: bar.popup().visible().to_string(),
45+
},
46+
SetExclusive { exclusive } => {
47+
bar.set_exclusive(*exclusive);
48+
Response::Ok
49+
}
50+
})
51+
.reduce(|acc, rsp| match (acc, rsp) {
52+
// If all responses are Ok, return one Ok. We assume we'll never mix Ok and OkValue.
53+
(Response::Ok, _) => Response::Ok,
54+
// Two or more OkValues create a multi:
55+
(Response::OkValue { value: v1 }, Response::OkValue { value: v2 }) => Response::Multi {
56+
values: vec![v1, v2],
57+
},
58+
(Response::Multi { mut values }, Response::OkValue { value: v }) => {
59+
values.push(v);
60+
Response::Multi { values }
61+
}
62+
_ => unreachable!(),
63+
})
64+
.unwrap_or(Response::error("Invalid bar name"))
5365
}
5466

5567
fn set_visible(bar: &Bar, visible: bool) -> Response {

Diff for: src/main.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -267,17 +267,18 @@ impl Ironbar {
267267
.clone()
268268
}
269269

270-
/// Gets a clone of a bar by its unique name.
270+
/// Gets clones of bars by their name.
271271
///
272-
/// Since the bar contains mostly GTK objects,
272+
/// Since the bars contain mostly GTK objects,
273273
/// the clone is cheap enough to not worry about.
274274
#[must_use]
275-
pub fn bar_by_name(&self, name: &str) -> Option<Bar> {
275+
pub fn bars_by_name(&self, name: &str) -> Vec<Bar> {
276276
self.bars
277277
.borrow()
278278
.iter()
279-
.find(|&bar| bar.name() == name)
279+
.filter(|&bar| bar.name() == name)
280280
.cloned()
281+
.collect()
281282
}
282283

283284
/// Re-reads the config file from disk and replaces the active config.

0 commit comments

Comments
 (0)