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
9pub 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 }
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}