tokio_dbus_xml/elements.rs
1use tokio_dbus_core::signature::Signature;
2
3/// A D-Bus node.
4#[derive(Debug, Clone, Default)]
5pub struct Node<'a> {
6 /// The name of the node, which for a root node is usually absent and for a
7 /// child node is a relative object path.
8 pub name: Option<&'a str>,
9 /// Interfaces in the node.
10 pub interfaces: Box<[Interface<'a>]>,
11 /// Sub-nodes in the node.
12 pub nodes: Box<[Node<'a>]>,
13}
14
15impl<'a> Node<'a> {
16 /// Find an interface by name.
17 ///
18 /// This searches the node and, recursively, all of its children, which makes
19 /// it possible to pick a single interface out of the introspection data of a
20 /// whole object tree.
21 pub fn interface(&self, name: &str) -> Option<&Interface<'a>> {
22 if let Some(interface) = self.interfaces.iter().find(|i| i.name == name) {
23 return Some(interface);
24 }
25
26 self.nodes.iter().find_map(|n| n.interface(name))
27 }
28
29 /// Iterate over every interface in this node and its children.
30 pub fn all_interfaces(&self) -> impl Iterator<Item = &Interface<'a>> {
31 let mut stack = vec![(self, 0usize)];
32
33 core::iter::from_fn(move || {
34 loop {
35 let (node, index) = stack.last_mut()?;
36
37 if let Some(interface) = node.interfaces.get(*index) {
38 *index += 1;
39 return Some(interface);
40 }
41
42 let node = *node;
43 stack.pop();
44 stack.extend(node.nodes.iter().map(|n| (n, 0)));
45 }
46 })
47 }
48}
49
50/// A single interface.
51#[derive(Debug, Clone)]
52pub struct Interface<'a> {
53 /// The name of the interface.
54 pub name: &'a str,
55 /// Methods associated with the interface.
56 pub methods: Box<[Method<'a>]>,
57 /// Signals associated with the interface.
58 pub signals: Box<[Signal<'a>]>,
59 /// Properties associated with the interface.
60 pub properties: Box<[Property<'a>]>,
61 /// Annotations applied to the interface.
62 pub annotations: Box<[Annotation<'a>]>,
63 /// Documentation associated with the interface.
64 pub doc: Doc<'a>,
65}
66
67/// The direction of an argument.
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub enum Direction {
70 /// Input argument.
71 In,
72 /// Output argument.
73 Out,
74}
75
76/// A method or signal argument.
77#[derive(Debug, Clone)]
78pub struct Argument<'a> {
79 /// The name of the argument.
80 pub name: Option<&'a str>,
81 /// The type of the argument.
82 pub ty: &'a Signature,
83 /// The direction of the argument.
84 ///
85 /// Arguments of a signal have no direction, and arguments of a method
86 /// default to [`Direction::In`] when it is absent.
87 pub direction: Direction,
88 /// Documentation associated with the argument.
89 pub doc: Doc<'a>,
90}
91
92/// A method on an interface.
93#[derive(Debug, Clone)]
94pub struct Method<'a> {
95 /// The name of the method.
96 pub name: &'a str,
97 /// Arguments to the method, in both directions.
98 pub arguments: Box<[Argument<'a>]>,
99 /// Annotations applied to the method.
100 pub annotations: Box<[Annotation<'a>]>,
101 /// Documentation associated with the method.
102 pub doc: Doc<'a>,
103}
104
105impl<'a> Method<'a> {
106 /// Iterate over the arguments the caller passes in.
107 pub fn inputs(&self) -> impl Iterator<Item = &Argument<'a>> {
108 self.arguments
109 .iter()
110 .filter(|a| a.direction == Direction::In)
111 }
112
113 /// Iterate over the arguments the method returns.
114 pub fn outputs(&self) -> impl Iterator<Item = &Argument<'a>> {
115 self.arguments
116 .iter()
117 .filter(|a| a.direction == Direction::Out)
118 }
119
120 /// Test if the method is annotated as not returning a reply.
121 pub fn no_reply(&self) -> bool {
122 self.annotations
123 .iter()
124 .any(|a| a.name == "org.freedesktop.DBus.Method.NoReply" && a.value == "true")
125 }
126}
127
128/// A signal emitted by an interface.
129#[derive(Debug, Clone)]
130pub struct Signal<'a> {
131 /// The name of the signal.
132 pub name: &'a str,
133 /// The arguments carried by the signal.
134 pub arguments: Box<[Argument<'a>]>,
135 /// Annotations applied to the signal.
136 pub annotations: Box<[Annotation<'a>]>,
137 /// Documentation associated with the signal.
138 pub doc: Doc<'a>,
139}
140
141/// Whether a property can be read, written, or both.
142#[derive(Debug, Clone, Copy, PartialEq, Eq)]
143pub enum Access {
144 /// The property can only be read.
145 Read,
146 /// The property can only be written.
147 Write,
148 /// The property can be both read and written.
149 ReadWrite,
150}
151
152impl Access {
153 /// Test if the property can be read.
154 pub fn is_readable(self) -> bool {
155 matches!(self, Access::Read | Access::ReadWrite)
156 }
157
158 /// Test if the property can be written.
159 pub fn is_writable(self) -> bool {
160 matches!(self, Access::Write | Access::ReadWrite)
161 }
162}
163
164/// A property on an interface.
165#[derive(Debug, Clone)]
166pub struct Property<'a> {
167 /// The name of the property.
168 pub name: &'a str,
169 /// The type of the property.
170 pub ty: &'a Signature,
171 /// Whether the property can be read, written, or both.
172 pub access: Access,
173 /// Annotations applied to the property.
174 pub annotations: Box<[Annotation<'a>]>,
175 /// Documentation associated with the property.
176 pub doc: Doc<'a>,
177}
178
179/// An annotation, which is a free form name and value pair attached to an
180/// element.
181#[derive(Debug, Clone, Copy)]
182pub struct Annotation<'a> {
183 /// The name of the annotation, such as `org.freedesktop.DBus.Deprecated`.
184 pub name: &'a str,
185 /// The value of the annotation.
186 pub value: &'a str,
187}
188
189/// Documentation associated with an element.
190#[derive(Debug, Default, Clone)]
191pub struct Doc<'a> {
192 /// Documentation summary.
193 pub summary: Option<&'a str>,
194 /// Description.
195 pub description: Description<'a>,
196}
197
198impl<'a> Doc<'a> {
199 /// Iterate over the lines of documentation, if there are any.
200 ///
201 /// Leading and trailing whitespace is trimmed from each line, since
202 /// documentation in an interface file is indented to match the surrounding
203 /// XML rather than the text.
204 pub fn lines(&self) -> impl Iterator<Item = &'a str> {
205 self.summary
206 .into_iter()
207 .chain(self.description.paragraph)
208 .flat_map(|text| text.lines())
209 .map(str::trim)
210 .filter(|line| !line.is_empty())
211 }
212
213 /// Test if there is no documentation at all.
214 pub fn is_empty(&self) -> bool {
215 self.summary.is_none() && self.description.paragraph.is_none()
216 }
217}
218
219/// The description of an element.
220#[derive(Debug, Default, Clone)]
221pub struct Description<'a> {
222 /// Paragraph describing an element.
223 pub paragraph: Option<&'a str>,
224}