Skip to content

Commit b34e04d

Browse files
committed
🐛 xmlgen: avoid generating duplicate receive_*_changed methods
When given an interface definition with a property named `Foo` and a signal named `FooChanged`, `zbus_xmlgen` would generate duplicate `receive_foo_changed` methods, causing compilation errors. In practice, the colliding `*Changed` signal is used to override the behavior of the property's implicit change signal, so it's preferable to use the overridden one and leave off the implicit rather than keeping it as a compilation error. Resolves `Case 2` of #231
1 parent 617ff16 commit b34e04d

3 files changed

Lines changed: 47 additions & 3 deletions

File tree

zbus_xmlgen/src/lib.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use snakecase::ascii::to_snakecase;
22
use std::{
3+
collections::HashSet,
34
error::Error,
45
fmt::{Display, Formatter, Write},
56
process::{Command, Stdio},
@@ -185,6 +186,11 @@ impl GenTrait<'_> {
185186

186187
let mut signals = iface.signals().to_vec();
187188
signals.sort_by(|a, b| a.name().partial_cmp(&b.name()).unwrap());
189+
190+
// collect signal method names for collision detection with implicit property change
191+
// receivers
192+
let mut changed_suffix_signals = HashSet::new();
193+
188194
for signal in &signals {
189195
let args = parse_signal_args(signal.args());
190196
let name = to_identifier(&to_snakecase(signal.name().as_str()));
@@ -196,20 +202,43 @@ impl GenTrait<'_> {
196202
writeln!(w, " #[zbus(signal)]")?;
197203
}
198204
writeln!(w, " fn {name}({args}) -> zbus::Result<()>;",)?;
205+
206+
if to_identifier(&to_snakecase(signal.name().as_str())).ends_with("_changed") {
207+
changed_suffix_signals.insert(signal.name());
208+
}
199209
}
200210

201211
let mut props = iface.properties().to_vec();
202212
props.sort_by(|a, b| a.name().partial_cmp(&b.name()).unwrap());
203213
for p in props {
204214
let name = to_identifier(&to_snakecase(p.name().as_str()));
205-
let fn_attribute = if pascal_case(&name) != p.name().as_str() {
206-
format!(" #[zbus(property, name = \"{}\")]", p.name())
215+
216+
let prop_change_receiver_name = format!("{name}_changed");
217+
let signal_collision = changed_suffix_signals
218+
.iter()
219+
.find(|s| to_identifier(&to_snakecase(s.as_str())) == prop_change_receiver_name);
220+
221+
let (property_args, comment) = if let Some(signal_collision) = signal_collision {
222+
(
223+
"(emits_changed_signal = \"false\")",
224+
format!(
225+
" // Note: change signal is shadowed by `{}` signal.\n",
226+
signal_collision.as_str()
227+
),
228+
)
229+
} else {
230+
("", "".to_string())
231+
};
232+
let name_arg = if pascal_case(&name) != p.name().as_str() {
233+
format!(", name = \"{}\"", p.name())
207234
} else {
208-
" #[zbus(property)]".to_string()
235+
"".to_string()
209236
};
237+
let fn_attribute = format!(" #[zbus(property{property_args}{name_arg})]");
210238

211239
writeln!(w)?;
212240
writeln!(w, " /// {} property", p.name())?;
241+
write!(w, "{comment}")?;
213242
if p.access().read() {
214243
writeln!(w, "{fn_attribute}")?;
215244
let output = to_rust_type(p.ty(), false, false);

zbus_xmlgen/tests/data/sample_object0.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ pub trait SampleInterface0 {
6868
#[zbus(signal)]
6969
fn signal_value(&self, value: zbus::zvariant::Value<'_>) -> zbus::Result<()>;
7070

71+
/// StateChanged signal
72+
#[zbus(signal)]
73+
fn state_changed(&self, state: u32, something_else: u32) -> zbus::Result<()>;
74+
7175
/// Bar property
7276
#[zbus(property)]
7377
fn bar(&self) -> zbus::Result<u8>;
@@ -94,4 +98,9 @@ pub trait SampleInterface0 {
9498
std::collections::HashMap<String, zbus::zvariant::OwnedValue>,
9599
)>,
96100
>;
101+
102+
/// State property
103+
// Note: change signal is shadowed by `StateChanged` signal.
104+
#[zbus(property(emits_changed_signal = "false"))]
105+
fn state(&self) -> zbus::Result<u32>;
97106
}

zbus_xmlgen/tests/data/sample_object0.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@
5252
<signal name="SignalDictStringToValue">
5353
<arg type="a{sv}" name="dict"/>
5454
</signal>
55+
<!-- Signal `StateChanged` should override `State`'s implicit `receive_state_changed` method -->
56+
<signal name="StateChanged">
57+
<arg name="state" type="u"/>
58+
<arg name="something_else" type="u"/>
59+
</signal>
60+
<property name="State" type="u" access="read"/>
5561
<property name="Bar" type="y" access="readwrite"/>
5662
<property name="Foo-Bar" type="y" access="readwrite"/>
5763
<property name="Matryoshkas" type="a(oiasta{sv})" access="read"/>

0 commit comments

Comments
 (0)