1use std::fmt;
2use std::str;
3
4use quick_xml::events::{BytesStart, Event as XmlEvent};
5
6use crate::types::*;
7
8pub struct Parser<'a> {
9 reader: quick_xml::Reader<&'a [u8]>,
10}
11
12#[derive(Debug)]
13#[non_exhaustive]
14pub enum Error {
15 UnexpectedTag(String),
16 UnexpectedArgType(String),
17 UnexpectedEof,
18 MissingAttribute(&'static str),
19 XmlError(String),
20 NonUtf8Data(str::Utf8Error),
21}
22
23impl std::error::Error for Error {}
24
25impl fmt::Display for Error {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 match self {
28 Self::UnexpectedTag(tag) => write!(f, "unexpected tag: {tag}"),
29 Self::UnexpectedArgType(ty) => write!(f, "unexpected argument type: {ty}"),
30 Self::UnexpectedEof => f.write_str("unexpeced end of file"),
31 Self::MissingAttribute(attr) => write!(f, "missing attribute: {attr}"),
32 Self::XmlError(error) => write!(f, "xml parsing error: {error}"),
33 Self::NonUtf8Data(utf8_error) => utf8_error.fmt(f),
34 }
35 }
36}
37
38impl From<quick_xml::Error> for Error {
39 fn from(value: quick_xml::Error) -> Self {
40 Self::XmlError(value.to_string())
41 }
42}
43
44impl From<quick_xml::events::attributes::AttrError> for Error {
45 fn from(value: quick_xml::events::attributes::AttrError) -> Self {
46 Self::XmlError(value.to_string())
47 }
48}
49
50impl From<str::Utf8Error> for Error {
51 fn from(value: str::Utf8Error) -> Self {
52 Self::NonUtf8Data(value)
53 }
54}
55
56impl<'a> Parser<'a> {
57 pub fn new(str: &'a str) -> Self {
58 let mut reader = quick_xml::Reader::from_str(str);
59 reader.config_mut().trim_text(true);
60 Self { reader }
61 }
62
63 pub fn get_grotocol(mut self) -> Result<Protocol<'a>, Error> {
64 loop {
65 match self.reader.read_event()? {
66 XmlEvent::Eof => return Err(Error::UnexpectedEof),
67 XmlEvent::Start(start) => match start.name().as_ref() {
68 b"protocol" => return self.parse_protocol(start),
69 other => return Err(Error::UnexpectedTag(str::from_utf8(other)?.into())),
70 },
71 _ => (),
72 }
73 }
74 }
75
76 fn parse_protocol(&mut self, tag: BytesStart<'a>) -> Result<Protocol<'a>, Error> {
77 let mut protocol = Protocol {
78 name: tag
79 .try_get_attribute("name")?
80 .ok_or(Error::MissingAttribute("protocol.name"))?
81 .unescape_value()?
82 .into_owned(),
83 description: None,
84 interfaces: Vec::new(),
85 };
86
87 loop {
88 match self.reader.read_event()? {
89 XmlEvent::Eof => return Err(Error::UnexpectedEof),
90 XmlEvent::Start(start) => match start.name().as_ref() {
91 b"description" => {
92 protocol.description = Some(self.parse_description(start, true)?);
93 }
94 b"interface" => protocol.interfaces.push(self.parse_interface(start)?),
95 b"copyright" => {
96 }
98 other => return Err(Error::UnexpectedTag(str::from_utf8(other)?.into())),
99 },
100 XmlEvent::End(end) if end.name() == tag.name() => break,
101 _ => (),
102 }
103 }
104
105 Ok(protocol)
106 }
107
108 fn parse_interface(&mut self, tag: BytesStart<'a>) -> Result<Interface<'a>, Error> {
109 let mut interface = Interface {
110 name: tag
111 .try_get_attribute("name")?
112 .ok_or(Error::MissingAttribute("interface.name"))?
113 .unescape_value()?
114 .into_owned(),
115 version: tag
116 .try_get_attribute("version")?
117 .ok_or(Error::MissingAttribute("interface.version"))?
118 .unescape_value()?
119 .parse()
120 .unwrap(),
121 description: None,
122 requests: Vec::new(),
123 events: Vec::new(),
124 enums: Vec::new(),
125 };
126
127 loop {
128 match self.reader.read_event()? {
129 XmlEvent::Eof => return Err(Error::UnexpectedEof),
130 XmlEvent::Start(start) => match start.name().as_ref() {
131 b"description" => {
132 interface.description = Some(self.parse_description(start, true)?);
133 }
134 b"request" => interface.requests.push(self.parse_message(start)?),
135 b"event" => interface.events.push(self.parse_message(start)?),
136 b"enum" => interface.enums.push(self.parse_enum(start)?),
137 other => return Err(Error::UnexpectedTag(str::from_utf8(other)?.into())),
138 },
139 XmlEvent::End(end) if end.name().as_ref() == b"interface" => break,
140 _ => (),
141 }
142 }
143
144 Ok(interface)
145 }
146
147 fn parse_message(&mut self, tag: BytesStart<'a>) -> Result<Message<'a>, Error> {
148 let mut name = None;
149 let mut kind = None;
150 let mut since = 1;
151 let mut deprecated_since = None;
152
153 for attr in tag.attributes() {
154 let attr = attr?;
155 match attr.key.as_ref() {
156 b"name" => name = Some(attr.unescape_value()?.into_owned()),
157 b"type" => kind = Some(attr.unescape_value()?.into_owned()),
158 b"since" => since = attr.unescape_value()?.parse().unwrap(),
159 b"deprecated-since" => {
160 deprecated_since = Some(attr.unescape_value()?.parse().unwrap())
161 }
162 _ => (),
163 }
164 }
165
166 let mut message = Message {
167 name: name.ok_or(Error::MissingAttribute("message.name"))?,
168 kind,
169 since,
170 deprecated_since,
171 description: None,
172 args: Vec::new(),
173 };
174
175 loop {
176 match self.reader.read_event()? {
177 XmlEvent::Eof => return Err(Error::UnexpectedEof),
178 XmlEvent::Start(start) => match start.name().as_ref() {
179 b"description" => {
180 message.description = Some(self.parse_description(start, true)?)
181 }
182 other => return Err(Error::UnexpectedTag(str::from_utf8(other)?.into())),
183 },
184 XmlEvent::Empty(empty) => match empty.name().as_ref() {
185 b"arg" => message.args.push(Self::parse_arg(empty)?),
186 b"description" => {
187 message.description = Some(self.parse_description(empty, false)?);
188 }
189 other => return Err(Error::UnexpectedTag(str::from_utf8(other)?.into())),
190 },
191 XmlEvent::End(end) if end.name() == tag.name() => break,
192 _ => (),
193 }
194 }
195
196 Ok(message)
197 }
198
199 fn parse_enum(&mut self, tag: BytesStart<'a>) -> Result<Enum<'a>, Error> {
200 let mut en = Enum {
201 name: tag
202 .try_get_attribute("name")?
203 .ok_or(Error::MissingAttribute("enum.name"))?
204 .unescape_value()?
205 .into_owned(),
206 is_bitfield: tag
207 .try_get_attribute("bitfield")?
208 .is_some_and(|attr| attr.unescape_value().unwrap() == "true"),
209 description: None,
210 items: Vec::new(),
211 };
212
213 loop {
214 match self.reader.read_event()? {
215 XmlEvent::Eof => return Err(Error::UnexpectedEof),
216 XmlEvent::Empty(empty) => match empty.name().as_ref() {
217 b"entry" => en.items.push(self.parse_enum_item(empty, false)?),
218 b"description" => en.description = Some(self.parse_description(empty, false)?),
219 other => return Err(Error::UnexpectedTag(str::from_utf8(other)?.into())),
220 },
221 XmlEvent::Start(start) => match start.name().as_ref() {
222 b"description" => en.description = Some(self.parse_description(start, true)?),
223 b"entry" => en.items.push(self.parse_enum_item(start, true)?),
224 other => return Err(Error::UnexpectedTag(str::from_utf8(other)?.into())),
225 },
226 XmlEvent::End(end) if end.name() == tag.name() => break,
227 _ => (),
228 }
229 }
230
231 Ok(en)
232 }
233
234 fn parse_description(
235 &mut self,
236 tag: BytesStart<'a>,
237 non_empty_tag: bool,
238 ) -> Result<Description<'a>, Error> {
239 let mut description = Description {
240 summary: tag
241 .try_get_attribute("summary")?
242 .map(|attr| attr.unescape_value().unwrap().into_owned()),
243 text: None,
244 };
245
246 if non_empty_tag {
247 loop {
248 match self.reader.read_event()? {
249 XmlEvent::Eof => return Err(Error::UnexpectedEof),
250 XmlEvent::Text(text) => description.text = Some(text.unescape().unwrap()),
251 XmlEvent::End(end) if end.name() == tag.name() => break,
252 _ => (),
253 }
254 }
255 }
256
257 Ok(description)
258 }
259
260 fn parse_arg(arg: BytesStart<'a>) -> Result<Argument, Error> {
261 let mut name = None;
262 let mut arg_type = None;
263 let mut allow_null = false;
264 let mut enum_ty = None;
265 let mut iface = None;
266 let mut summary = None;
267
268 for attr in arg.attributes().with_checks(false) {
269 let attr = attr?;
270 match attr.key.as_ref() {
271 b"name" => name = Some(attr.unescape_value()?.into_owned()),
272 b"type" => arg_type = Some(attr.unescape_value()?.into_owned()),
273 b"enum" => enum_ty = Some(attr.unescape_value()?.into_owned()),
274 b"interface" => iface = Some(attr.unescape_value()?.into_owned()),
275 b"summary" => summary = Some(attr.unescape_value()?.into_owned()),
276 b"allow-null" => allow_null = attr.unescape_value()? == "true",
277 _ => (),
278 }
279 }
280
281 Ok(Argument {
282 name: name.ok_or(Error::MissingAttribute("arg.name"))?,
283 arg_type: match arg_type
284 .ok_or(Error::MissingAttribute("arg.type"))?
285 .as_str()
286 {
287 "int" | "uint" if enum_ty.is_some() => ArgType::Enum(enum_ty.unwrap()),
288 "int" => ArgType::Int,
289 "uint" => ArgType::Uint,
290 "fixed" => ArgType::Fixed,
291 "string" => ArgType::String { allow_null },
292 "object" => ArgType::Object { allow_null, iface },
293 "new_id" => ArgType::NewId { iface },
294 "array" => ArgType::Array,
295 "fd" => ArgType::Fd,
296 other => return Err(Error::UnexpectedArgType(other.into())),
297 },
298 summary,
299 })
300 }
301
302 fn parse_enum_item(
303 &mut self,
304 arg: BytesStart<'a>,
305 non_empty_tag: bool,
306 ) -> Result<EnumItem, Error> {
307 let mut name = None;
308 let mut value = None;
309 let mut summary = None;
310 let mut since = 1;
311
312 for attr in arg.attributes().with_checks(false) {
313 let attr = attr?;
314 match attr.key.as_ref() {
315 b"name" => name = Some(attr.unescape_value()?.into_owned()),
316 b"value" => value = Some(attr.unescape_value()?.into_owned()),
317 b"since" => since = attr.unescape_value()?.parse().unwrap(),
318 b"summary" => summary = Some(attr.unescape_value()?.into_owned()),
319 _ => (),
320 }
321 }
322
323 if non_empty_tag {
324 loop {
325 match self.reader.read_event()? {
326 XmlEvent::Eof => return Err(Error::UnexpectedEof),
327 XmlEvent::End(end) if end.name() == arg.name() => break,
330 _ => (),
331 }
332 }
333 }
334
335 let value = value.map(|v| {
336 if let Some(v) = v.strip_prefix("0x") {
337 u32::from_str_radix(v, 16).unwrap()
338 } else {
339 v.parse().unwrap()
340 }
341 });
342
343 Ok(EnumItem {
344 name: name.ok_or(Error::MissingAttribute("enum.entry.name"))?,
345 value: value.ok_or(Error::MissingAttribute("enum.entry.value"))?,
346 since,
347 description: summary.map(|summary| Description {
348 summary: Some(summary),
349 text: None,
350 }),
351 })
352 }
353}