wayrs_proto_parser/
parser.rs

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                        // TODO?
97                    }
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                    // TODO
328                    // XmlEvent::Text(text) => description.text = Some(text.unescape().unwrap()),
329                    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}