Skip to content

Commit b8ea635

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 b8ea635

3 files changed

Lines changed: 48 additions & 3 deletions

File tree

zbus_xmlgen/src/lib.rs

Lines changed: 33 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::HashMap,
34
error::Error,
45
fmt::{Display, Formatter, Write},
56
process::{Command, Stdio},
@@ -185,6 +186,19 @@ 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 signal_method_names: HashMap<String, String> = signals
193+
.iter()
194+
.map(|s| {
195+
(
196+
to_identifier(&to_snakecase(s.name().as_str())),
197+
s.name().as_str().to_owned(),
198+
)
199+
})
200+
.collect();
201+
188202
for signal in &signals {
189203
let args = parse_signal_args(signal.args());
190204
let name = to_identifier(&to_snakecase(signal.name().as_str()));
@@ -202,14 +216,30 @@ impl GenTrait<'_> {
202216
props.sort_by(|a, b| a.name().partial_cmp(&b.name()).unwrap());
203217
for p in props {
204218
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())
219+
220+
let prop_change_receiver_name = format!("{name}_changed");
221+
let signal_collision = signal_method_names.get(&prop_change_receiver_name);
222+
223+
let property_args = if signal_collision.is_some() {
224+
"(emits_changed_signal = \"false\")"
225+
} else {
226+
""
227+
};
228+
let name_arg = if pascal_case(&name) != p.name().as_str() {
229+
format!(", name = \"{}\"", p.name())
207230
} else {
208-
" #[zbus(property)]".to_string()
231+
"".to_string()
209232
};
233+
let fn_attribute = format!(" #[zbus(property{property_args}{name_arg})]");
210234

211235
writeln!(w)?;
212236
writeln!(w, " /// {} property", p.name())?;
237+
if let Some(signal_collision) = signal_collision {
238+
writeln!(
239+
w,
240+
" // Note: change signal is shadowed by imported `{signal_collision}` signal"
241+
)?;
242+
}
213243
if p.access().read() {
214244
writeln!(w, "{fn_attribute}")?;
215245
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 imported `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)