1use std::io::{BufRead, BufReader, Read};
2
3use quick_xml::{
4 events::{attributes::Attributes, Event},
5 reader::Reader,
6};
7use thiserror::Error;
8
9use super::protocol::*;
10#[derive(Error, Debug)]
11pub enum Error {
12 #[error("Ill-formed protocol file")]
13 IllFormed,
14 #[error("Protocol must have a name")]
15 NoName,
16 #[error("Invalid UTF-8: {0}")]
17 FromUtf8(#[from] std::string::FromUtf8Error),
18 #[error("Invalid UTF-8: {0}")]
19 Utf8(#[from] std::str::Utf8Error),
20 #[error("Invalid number: {0}")]
21 ParseInt(#[from] std::num::ParseIntError),
22 #[error("Invalid XML")]
23 Xml(#[from] quick_xml::Error),
24 #[error("Unexpected token: `{0}`")]
25 UnexpectedToken(String),
26 #[error("Unexpected type: `{0}`")]
27 UnexpectedType(String),
28 #[error("Unexpected end tag, expected: `{expected}`, got: `{actual}`")]
29 UnexpectedEndTag { expected: String, actual: String },
30}
31type Result<T, E = Error> = std::result::Result<T, E>;
32
33fn extract_end_tag<R: BufRead>(reader: &mut Reader<R>, tag: &[u8]) -> Result<()> {
34 match reader.read_event_into(&mut Vec::new())? {
35 Event::End(bytes) =>
36 if bytes.name().local_name().as_ref() != tag {
37 return Err(Error::UnexpectedEndTag {
38 expected: String::from_utf8_lossy(tag).into_owned(),
39 actual: String::from_utf8_lossy(bytes.name().local_name().as_ref())
40 .into_owned(),
41 })
42 },
43 other => return Err(Error::UnexpectedToken(format!("{other:?}"))),
44 }
45 Ok(())
46}
47pub fn parse<S: Read>(stream: S) -> Result<Protocol> {
48 let mut reader = Reader::from_reader(BufReader::new(stream));
49 reader.trim_text(true).expand_empty_elements(true);
50 let _ = reader.read_event_into(&mut Vec::new());
52 parse_protocol(reader)
53}
54
55fn parse_protocol<R: BufRead>(mut reader: Reader<R>) -> Result<Protocol> {
56 let mut protocol = match reader.read_event_into(&mut Vec::new())? {
57 Event::Start(bytes) => {
58 assert!(
59 bytes.local_name().as_ref() == b"protocol",
60 "Missing protocol toplevel tag"
61 );
62 if let Some(attr) = bytes
63 .attributes()
64 .filter_map(|res| res.ok())
65 .find(|attr| attr.key.local_name().as_ref() == b"name")
66 {
67 Protocol::new(String::from_utf8(attr.value.into_owned())?)
68 } else {
69 return Err(Error::NoName)
70 }
71 },
72 other => return Err(Error::UnexpectedToken(format!("{other:?}"))),
73 };
74
75 loop {
76 match reader.read_event_into(&mut Vec::new()) {
77 Ok(Event::Start(bytes)) => {
78 match bytes.local_name().as_ref() {
79 b"copyright" => {
80 let copyright = match reader.read_event_into(&mut Vec::new()) {
82 Ok(Event::Text(copyright)) =>
83 copyright.unescape().ok().map(std::borrow::Cow::into_owned),
84 Ok(Event::CData(copyright)) =>
85 String::from_utf8(copyright.into_inner().into()).ok(),
86 Err(e) => return Err(e.into()),
87 _ => return Err(Error::IllFormed),
88 };
89
90 extract_end_tag(&mut reader, b"copyright")?;
91 protocol.copyright = copyright
92 },
93 b"interface" => {
94 protocol
95 .interfaces
96 .push(parse_interface(&mut reader, bytes.attributes())?);
97 },
98 b"description" => {
99 protocol.description =
100 Some(parse_description(&mut reader, bytes.attributes())?);
101 },
102 _ =>
103 return Err(Error::UnexpectedToken(String::from_utf8(
104 bytes.local_name().as_ref().to_owned(),
105 )?)),
106 }
107 },
108 Ok(Event::End(bytes)) => {
109 assert!(
110 bytes.local_name().as_ref() == b"protocol",
111 "Unexpected closing token `{}`",
112 String::from_utf8_lossy(bytes.local_name().as_ref())
113 );
114 break
115 },
116 Ok(Event::Comment(_)) => {},
118 e => return Err(Error::UnexpectedToken(format!("{e:?}"))),
119 }
120 }
121
122 Ok(protocol)
123}
124
125fn parse_interface<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Result<Interface> {
126 let mut interface = Interface::new();
127 for attr in attrs.filter_map(|res| res.ok()) {
128 match attr.key.local_name().as_ref() {
129 b"name" => interface.name = String::from_utf8(attr.value.into_owned())?,
130 b"version" => interface.version = std::str::from_utf8(attr.value.as_ref())?.parse()?,
131 _ => {},
132 }
133 }
134
135 loop {
136 match reader.read_event_into(&mut Vec::new()) {
137 Ok(Event::Start(bytes)) => match bytes.local_name().as_ref() {
138 b"description" =>
139 interface.description = Some(parse_description(reader, bytes.attributes())?),
140 b"request" => interface.requests.push(parse_event_or_request(
141 reader,
142 bytes.attributes(),
143 b"request",
144 )?),
145 b"event" => interface.events.push(parse_event_or_request(
146 reader,
147 bytes.attributes(),
148 b"event",
149 )?),
150 b"enum" => interface
151 .enums
152 .push(parse_enum(reader, bytes.attributes())?),
153 _ =>
154 return Err(Error::UnexpectedToken(String::from_utf8(
155 bytes.local_name().as_ref().to_owned(),
156 )?)),
157 },
158 Ok(Event::End(bytes)) if bytes.local_name().as_ref() == b"interface" => break,
159 _ => {},
160 }
161 }
162
163 Ok(interface)
164}
165
166fn parse_description<R: BufRead>(
167 reader: &mut Reader<R>,
168 attrs: Attributes,
169) -> Result<(String, String)> {
170 let mut summary = String::new();
171 for attr in attrs.filter_map(|res| res.ok()) {
172 if attr.key.local_name().as_ref() == b"summary" {
173 summary = String::from_utf8_lossy(&attr.value)
174 .split_whitespace()
175 .collect::<Vec<_>>()
176 .join(" ");
177 }
178 }
179
180 let mut description = String::new();
181 loop {
184 match reader.read_event_into(&mut Vec::new()) {
185 Ok(Event::Text(bytes)) => {
186 if !description.is_empty() {
187 description.push_str("\n\n");
188 }
189 description.push_str(&bytes.unescape().unwrap_or_default())
190 },
191 Ok(Event::End(bytes)) if bytes.local_name().as_ref() == b"description" => break,
192 Ok(Event::Comment(_)) => {},
193 Err(e) => return Err(e.into()),
194 e => return Err(Error::UnexpectedToken(format!("{e:?}"))),
195 }
196 }
197
198 Ok((summary, description))
199}
200
201fn parse_enum<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Result<Enum> {
202 let mut enu = Enum::new();
203 for attr in attrs.filter_map(|res| res.ok()) {
204 match attr.key.local_name().as_ref() {
205 b"name" => enu.name = String::from_utf8(attr.value.into_owned())?,
206 b"since" => enu.since = std::str::from_utf8(&attr.value)?.parse()?,
207 b"bitfield" =>
208 if &attr.value[..] == b"true" {
209 enu.bitfield = true
210 },
211 _ => {},
212 }
213 }
214
215 loop {
216 match reader.read_event_into(&mut Vec::new()) {
217 Ok(Event::Start(bytes)) => match bytes.local_name().as_ref() {
218 b"description" =>
219 enu.description = Some(parse_description(reader, bytes.attributes())?),
220 b"entry" => enu.entries.push(parse_entry(reader, bytes.attributes())?),
221 _ =>
222 return Err(Error::UnexpectedToken(String::from_utf8(
223 bytes.local_name().as_ref().to_owned(),
224 )?)),
225 },
226 Ok(Event::End(bytes)) if bytes.local_name().as_ref() == b"enum" => break,
227 _ => {},
228 }
229 }
230
231 Ok(enu)
232}
233
234fn parse_event_or_request<R: BufRead>(
235 reader: &mut Reader<R>,
236 attrs: Attributes,
237 tag: &[u8],
238) -> Result<Message> {
239 let mut message = Message::new();
240 for attr in attrs.filter_map(|res| res.ok()) {
241 match attr.key.local_name().as_ref() {
242 b"name" => message.name = String::from_utf8(attr.value.into_owned())?,
243 b"type" => message.typ = Some(parse_type(&attr.value)?),
244 b"since" => message.since = std::str::from_utf8(&attr.value)?.parse()?,
245 _ => {},
246 }
247 }
248
249 loop {
250 match reader.read_event_into(&mut Vec::new()) {
251 Ok(Event::Start(bytes)) => match bytes.local_name().as_ref() {
252 b"description" =>
253 message.description = Some(parse_description(reader, bytes.attributes())?),
254 b"arg" => {
255 let arg = parse_arg(reader, bytes.attributes())?;
256 if arg.typ == Type::NewId && arg.interface.is_none() {
257 message.args.push(Arg {
259 name: "interface".to_string(),
260 typ: Type::String,
261 interface: None,
262 summary: Some("interface of the bound object".into()),
263 description: None,
264 allow_null: false,
265 enum_: None,
266 });
267 message.args.push(Arg {
268 name: "version".to_string(),
269 typ: Type::Uint,
270 interface: None,
271 summary: Some("interface version of the bound object".into()),
272 description: None,
273 allow_null: false,
274 enum_: None,
275 });
276 }
277 message.args.push(arg);
278 },
279 _ =>
280 return Err(Error::UnexpectedToken(String::from_utf8(
281 bytes.local_name().as_ref().to_owned(),
282 )?)),
283 },
284 Ok(Event::End(bytes)) if bytes.local_name().as_ref() == tag => break,
285 _ => {},
286 }
287 }
288
289 Ok(message)
290}
291
292fn parse_arg<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Result<Arg> {
293 let mut arg = Arg::new();
294 for attr in attrs.filter_map(|res| res.ok()) {
295 match attr.key.local_name().as_ref() {
296 b"name" => arg.name = String::from_utf8(attr.value.into_owned())?,
297 b"type" => arg.typ = parse_type(&attr.value)?,
298 b"summary" =>
299 arg.summary = Some(
300 String::from_utf8_lossy(&attr.value)
301 .split_whitespace()
302 .collect::<Vec<_>>()
303 .join(" "),
304 ),
305 b"interface" => arg.interface = Some(String::from_utf8(attr.value.into_owned())?),
306 b"allow-null" =>
307 if &*attr.value == b"true" {
308 arg.allow_null = true
309 },
310 b"enum" => arg.enum_ = Some(String::from_utf8(attr.value.into_owned())?),
311 _ => {},
312 }
313 }
314
315 loop {
316 match reader.read_event_into(&mut Vec::new()) {
317 Ok(Event::Start(bytes)) => match bytes.local_name().as_ref() {
318 b"description" =>
319 arg.description = Some(parse_description(reader, bytes.attributes())?),
320 _ =>
321 return Err(Error::UnexpectedToken(String::from_utf8(
322 bytes.local_name().as_ref().to_owned(),
323 )?)),
324 },
325 Ok(Event::End(bytes)) if bytes.local_name().as_ref() == b"arg" => break,
326 _ => {},
327 }
328 }
329
330 Ok(arg)
331}
332
333fn parse_type(txt: &[u8]) -> Result<Type> {
334 Ok(match txt {
335 b"int" => Type::Int,
336 b"uint" => Type::Uint,
337 b"fixed" => Type::Fixed,
338 b"string" => Type::String,
339 b"object" => Type::Object,
340 b"new_id" => Type::NewId,
341 b"array" => Type::Array,
342 b"fd" => Type::Fd,
343 b"destructor" => Type::Destructor,
344 e =>
345 return Err(Error::UnexpectedType(
346 String::from_utf8_lossy(e).into_owned(),
347 )),
348 })
349}
350
351fn parse_entry<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Result<Entry> {
352 let mut entry = Entry::new();
353 for attr in attrs.filter_map(|res| res.ok()) {
354 match attr.key.local_name().as_ref() {
355 b"name" => entry.name = String::from_utf8(attr.value.into_owned())?,
356 b"value" => {
357 entry.value = if attr.value.starts_with(b"0x") {
358 u32::from_str_radix(std::str::from_utf8(&attr.value[2..])?, 16)?
359 } else {
360 std::str::from_utf8(&attr.value)?.parse()?
361 };
362 },
363 b"since" => entry.since = std::str::from_utf8(&attr.value)?.parse()?,
364 b"summary" =>
365 entry.summary = Some(
366 String::from_utf8_lossy(&attr.value)
367 .split_whitespace()
368 .collect::<Vec<_>>()
369 .join(" "),
370 ),
371 _ => {},
372 }
373 }
374
375 loop {
376 match reader.read_event_into(&mut Vec::new()) {
377 Ok(Event::Start(bytes)) => match bytes.local_name().as_ref() {
378 b"description" =>
379 entry.description = Some(parse_description(reader, bytes.attributes())?),
380 _ =>
381 return Err(Error::UnexpectedToken(
382 String::from_utf8_lossy(bytes.local_name().as_ref()).into_owned(),
383 )),
384 },
385 Ok(Event::End(bytes)) if bytes.local_name().as_ref() == b"entry" => break,
386 _ => {},
387 }
388 }
389
390 Ok(entry)
391}