Skip to main content

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}