Skip to content

Commit

Permalink
Merge pull request #504 from EHfive/fix-introspect
Browse files Browse the repository at this point in the history
🐛 zb: use the correct traversal order for node tree introspection
  • Loading branch information
zeenix authored Nov 5, 2023
2 parents e4e9276 + 562876b commit f1c1550
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 28 deletions.
86 changes: 58 additions & 28 deletions zbus/src/object_server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use event_listener::{Event, EventListener};
use serde::Serialize;
use std::{
collections::{hash_map::Entry, HashMap, VecDeque},
collections::{hash_map::Entry, HashMap},
fmt::Write,
marker::PhantomData,
ops::{Deref, DerefMut},
Expand Down Expand Up @@ -298,38 +298,68 @@ impl Node {
}

async fn introspect_to_writer<W: Write + Send>(&self, writer: &mut W) {
let mut node_list = VecDeque::new();
node_list.push_back((self, "", 0));
while let Some((node, path, level)) = node_list.pop_front() {
if level == 0 {
writeln!(
writer,
r#"
enum Fragment<'a> {
/// Represent an unclosed node tree, could be further splitted into sub-`Fragment`s
Node {
name: &'a str,
node: &'a Node,
level: usize,
},
/// Represent a closing `</node>`
End { level: usize },
}

let mut stack = Vec::new();
stack.push(Fragment::Node {
name: "",
node: self,
level: 0,
});

// This can be seen as traversing the fragment tree in pre-order DFS with formatted XML
// fragment, splitted `Fragment::Node`s and `Fragment::End` being current node, left
// subtree and right leaf respectively.
while let Some(fragment) = stack.pop() {
match fragment {
Fragment::Node { name, node, level } => {
stack.push(Fragment::End { level });

for (name, node) in &node.children {
stack.push(Fragment::Node {
name,
node,
level: level + 2,
})
}

if level == 0 {
writeln!(
writer,
r#"
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>"#
)
.unwrap();
} else {
writeln!(
writer,
"{:indent$}<node name=\"{}\">",
"",
path,
indent = level
)
.unwrap();
}

for iface in node.interfaces.values() {
iface.read().await.introspect_to_writer(writer, level + 2);
}
)
.unwrap();
} else {
writeln!(
writer,
"{:indent$}<node name=\"{}\">",
"",
name,
indent = level
)
.unwrap();
}

for (path, node) in &node.children {
node_list.push_front((node, path, level + 2));
for iface in node.interfaces.values() {
iface.read().await.introspect_to_writer(writer, level + 2);
}
}
Fragment::End { level } => {
writeln!(writer, "{:indent$}</node>", "", indent = level).unwrap();
}
}

writeln!(writer, "{:indent$}</node>", "", indent = level).unwrap();
}
}

Expand Down
23 changes: 23 additions & 0 deletions zbus/tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,29 @@ async fn my_iface_test(conn: Connection, event: Event) -> zbus::Result<u32> {
}
drop(stream);

let root_introspect_proxy = zbus::fdo::IntrospectableProxy::builder(&conn)
.destination("org.freedesktop.MyService")?
.path("/")?
.build()
.await?;
debug!("Created: {:?}", root_introspect_proxy);

let root_xml = root_introspect_proxy.introspect().await?;
let root_node = zbus_xml::Node::from_reader(root_xml.as_bytes())
.map_err(|e| Error::Failure(e.to_string()))?;
let mut node = &root_node;
for name in ["org", "freedesktop", "MyService"] {
node = node
.nodes()
.iter()
.find(|&n| n.name().is_some_and(|n| n == name))
.expect("Child node not exist");
}
assert!(node
.interfaces()
.iter()
.any(|i| i.name() == "org.freedesktop.MyIface"));

let proxy = MyIfaceProxy::builder(&conn)
.destination("org.freedesktop.MyService")?
.path("/org/freedesktop/MyService")?
Expand Down

0 comments on commit f1c1550

Please sign in to comment.