tokio_dbus_xml/
parser.rs

1use std::fmt::Write;
2
3use tokio_dbus_core::signature::Signature;
4use xmlparser::{ElementEnd, Token};
5
6use crate::error::ErrorKind;
7use crate::{Argument, Description, Direction, Doc, Error, Interface, Method, Node, Result};
8
9/// Parse the contents of an interface file.
10pub fn parse_interface(interface: &str) -> Result<Node<'_>> {
11    let tokenizer = xmlparser::Tokenizer::from(interface);
12
13    let mut stack = vec![];
14    let mut path = String::new();
15    let mut root = NodeBuilder::default();
16
17    macro_rules! expect_end {
18        ($end:expr, $expected:literal) => {
19            if let Some(end) = $end {
20                if end != $expected {
21                    return Err(Error::new(
22                        path,
23                        ErrorKind::MismatchingEnd {
24                            expected: $expected.into(),
25                            actual: end.into(),
26                        },
27                    ));
28                }
29            }
30        };
31    }
32
33    for token in tokenizer {
34        let token = match token {
35            Ok(token) => token,
36            Err(error) => return Err(Error::new(path, error)),
37        };
38
39        match token {
40            Token::ElementStart { local, .. } => {
41                match (stack.last(), local.as_str()) {
42                    (None, "node") => {
43                        stack.push(State::Node(NodeBuilder::default()));
44                    }
45                    (Some(State::Node(..)), "interface") => {
46                        stack.push(State::Interface(InterfaceBuilder::default()));
47                    }
48                    (Some(State::Interface(..)), "method") => {
49                        stack.push(State::Method(MethodBuilder::default()));
50                    }
51                    (Some(State::Method(..)), "arg") => {
52                        stack.push(State::Argument(ArgumentBuilder::default()));
53                    }
54                    (Some(State::Argument(..) | State::Method(..)), "doc") => {
55                        stack.push(State::Doc(Doc::default()));
56                    }
57                    (Some(State::Doc(..)), "summary") => {
58                        stack.push(State::String("summary", StringBuilder::default()));
59                    }
60                    (Some(State::Doc(..)), "description") => {
61                        stack.push(State::Description(Description::default()));
62                    }
63                    (Some(State::Description(..)), "para") => {
64                        stack.push(State::String("para", StringBuilder::default()));
65                    }
66                    (_, element) => {
67                        return Err(Error::new(
68                            path,
69                            ErrorKind::UnsupportedElementStart(element.into()),
70                        ));
71                    }
72                }
73
74                if !path.is_empty() {
75                    path.push('/');
76                }
77
78                path.push_str(local.as_str());
79
80                match &stack[..] {
81                    [.., State::Node(node), State::Node(..)] => {
82                        let _ = write!(path, "[{}]", node.nodes.len());
83                    }
84                    [.., State::Node(node), State::Interface(..)] => {
85                        let _ = write!(path, "[{}]", node.interfaces.len());
86                    }
87                    [.., State::Interface(interface), State::Method(..)] => {
88                        let _ = write!(path, "[{}]", interface.methods.len());
89                    }
90                    [.., State::Method(method), State::Argument(..)] => {
91                        let _ = write!(path, "[{}]", method.arguments.len());
92                    }
93                    _ => {}
94                }
95            }
96            Token::ElementEnd { end, .. } => {
97                let name = match end {
98                    ElementEnd::Open => {
99                        continue;
100                    }
101                    ElementEnd::Close(_, name) => Some(name.as_str()),
102                    ElementEnd::Empty => None,
103                };
104
105                let Some(top) = stack.pop() else {
106                    return Err(Error::new(path, ErrorKind::UnsupportedElementEnd));
107                };
108
109                match (&mut stack[..], top) {
110                    ([], State::Node(node)) => {
111                        expect_end!(name, "node");
112                        root.interfaces.extend(node.interfaces);
113                        root.nodes.extend(node.nodes);
114                    }
115                    ([.., State::Node(node)], State::Interface(builder)) => {
116                        expect_end!(name, "interface");
117                        node.interfaces.push(
118                            builder
119                                .build()
120                                .map_err(|kind| Error::new(path.as_str(), kind))?,
121                        );
122                    }
123                    ([.., State::Node(node)], State::Node(builder)) => {
124                        expect_end!(name, "interface");
125                        node.nodes.push(builder.build());
126                    }
127                    ([.., State::Interface(interface)], State::Method(builder)) => {
128                        expect_end!(name, "method");
129                        interface.methods.push(
130                            builder
131                                .build()
132                                .map_err(|kind| Error::new(path.as_str(), kind))?,
133                        );
134                    }
135                    ([.., State::Method(method)], State::Argument(builder)) => {
136                        expect_end!(name, "arg");
137                        method.arguments.push(
138                            builder
139                                .build()
140                                .map_err(|kind| Error::new(path.as_str(), kind))?,
141                        );
142                    }
143                    ([.., State::Argument(argument)], State::Doc(doc)) => {
144                        expect_end!(name, "doc");
145                        argument.doc = doc;
146                    }
147                    ([.., State::Method(method)], State::Doc(doc)) => {
148                        expect_end!(name, "doc");
149                        method.doc = doc;
150                    }
151                    ([.., State::Doc(doc)], State::String("summary", string)) => {
152                        expect_end!(name, "summary");
153                        doc.summary = string.text;
154                    }
155                    ([.., State::Doc(doc)], State::Description(description)) => {
156                        expect_end!(name, "description");
157                        doc.description = description;
158                    }
159                    ([.., State::Description(description)], State::String("para", string)) => {
160                        expect_end!(name, "para");
161                        description.paragraph = string.text;
162                    }
163                    _ => return Err(Error::new(path, ErrorKind::UnsupportedElementEnd)),
164                }
165
166                if let Some(index) = path.rfind('/') {
167                    path.truncate(index);
168                } else {
169                    path.clear();
170                }
171            }
172            Token::Attribute {
173                prefix,
174                local,
175                value,
176                ..
177            } => {
178                let len = path.len();
179                path.push(':');
180                path.push_str(local.as_str());
181
182                match (&mut stack[..], prefix.as_str(), local.as_str()) {
183                    ([State::Node(..)], "xmlns", _) => {
184                        // ignore xmlns attributes, while these would be good to
185                        // validate, in practice they don't make much of a
186                        // difference and are rarely used.
187                    }
188                    ([.., State::Interface(builder)], _, "name") => {
189                        builder.name = Some(value.as_str());
190                    }
191                    ([.., State::Method(builder)], _, "name") => {
192                        builder.name = Some(value.as_str());
193                    }
194                    ([.., State::Argument(builder)], _, "name") => {
195                        builder.name = Some(value.as_str());
196                    }
197                    ([.., State::Argument(builder)], _, "direction") => {
198                        builder.direction = Some(match value.as_str() {
199                            "in" => Direction::In,
200                            "out" => Direction::Out,
201                            other => {
202                                return Err(Error::new(
203                                    path,
204                                    ErrorKind::UnsupportedArgumentDirection(other.into()),
205                                ))
206                            }
207                        });
208                    }
209                    ([.., State::Argument(builder)], _, "type") => {
210                        builder.ty = Some(
211                            Signature::new(value.as_str())
212                                .map_err(|kind| Error::new(path.as_str(), kind))?,
213                        );
214                    }
215                    (_, _, name) => {
216                        return Err(Error::new(
217                            path,
218                            ErrorKind::UnsupportedAttribute(name.into()),
219                        ));
220                    }
221                }
222
223                path.truncate(len);
224            }
225            Token::Text { text } => match stack.last_mut() {
226                Some(State::String(_, string)) => {
227                    string.text = Some(text.as_str());
228                }
229                _ => {
230                    if !text.as_str().trim().is_empty() {
231                        return Err(Error::new(path, ErrorKind::UnsupportedText));
232                    }
233                }
234            },
235            _ => {}
236        }
237    }
238
239    Ok(root.build())
240}
241
242#[derive(Debug, Default)]
243struct NodeBuilder<'a> {
244    interfaces: Vec<Interface<'a>>,
245    nodes: Vec<Node<'a>>,
246}
247
248impl<'a> NodeBuilder<'a> {
249    fn build(self) -> Node<'a> {
250        Node {
251            interfaces: self.interfaces.into(),
252            nodes: self.nodes.into(),
253        }
254    }
255}
256
257#[derive(Debug, Default)]
258struct InterfaceBuilder<'a> {
259    name: Option<&'a str>,
260    methods: Vec<Method<'a>>,
261}
262
263impl<'a> InterfaceBuilder<'a> {
264    fn build(self) -> Result<Interface<'a>, ErrorKind> {
265        let name = self.name.ok_or(ErrorKind::MissingInterfaceName)?;
266        Ok(Interface {
267            name,
268            methods: self.methods.into(),
269        })
270    }
271}
272
273#[derive(Debug, Default)]
274struct MethodBuilder<'a> {
275    name: Option<&'a str>,
276    arguments: Vec<Argument<'a>>,
277    doc: Doc<'a>,
278}
279
280impl<'a> MethodBuilder<'a> {
281    fn build(self) -> Result<Method<'a>, ErrorKind> {
282        let name = self.name.ok_or(ErrorKind::MissingMethodName)?;
283        Ok(Method {
284            name,
285            arguments: self.arguments.into(),
286        })
287    }
288}
289
290#[derive(Debug, Default)]
291struct ArgumentBuilder<'a> {
292    name: Option<&'a str>,
293    ty: Option<&'a Signature>,
294    direction: Option<Direction>,
295    doc: Doc<'a>,
296}
297
298impl<'a> ArgumentBuilder<'a> {
299    fn build(self) -> Result<Argument<'a>, ErrorKind> {
300        let ty = self.ty.ok_or(ErrorKind::MissingArgumentType)?;
301        let direction = self.direction.ok_or(ErrorKind::MissingArgumentDirection)?;
302
303        Ok(Argument {
304            name: self.name,
305            ty,
306            direction,
307        })
308    }
309}
310
311#[derive(Debug, Default)]
312struct StringBuilder<'a> {
313    text: Option<&'a str>,
314}
315
316#[derive(Debug)]
317enum State<'a> {
318    Node(NodeBuilder<'a>),
319    Interface(InterfaceBuilder<'a>),
320    Method(MethodBuilder<'a>),
321    Argument(ArgumentBuilder<'a>),
322    Doc(Doc<'a>),
323    Description(Description<'a>),
324    String(&'static str, StringBuilder<'a>),
325}