1use super::protocol::*;
2use std::{
3 io::{BufRead, BufReader, Read},
4 str::FromStr,
5};
6
7use quick_xml::{
8 events::{attributes::Attributes, Event},
9 Reader,
10};
11
12pub fn parse<S: Read>(stream: S) -> Protocol {
13 let mut reader = Reader::from_reader(BufReader::new(stream));
14 let reader_config = reader.config_mut();
15 reader_config.trim_text(true);
16 reader_config.expand_empty_elements = true;
17 parse_protocol(reader)
18}
19
20fn decode_utf8_or_panic(txt: Vec<u8>) -> String {
21 match String::from_utf8(txt) {
22 Ok(txt) => txt,
23 Err(e) => panic!("Invalid UTF8: '{}'", String::from_utf8_lossy(&e.into_bytes())),
24 }
25}
26
27fn parse_or_panic<T: FromStr>(txt: &[u8]) -> T {
28 match std::str::from_utf8(txt).ok().and_then(|val| val.parse().ok()) {
29 Some(version) => version,
30 None => panic!(
31 "Invalid value '{}' for parsing type '{}'",
32 String::from_utf8_lossy(txt),
33 std::any::type_name::<T>()
34 ),
35 }
36}
37
38fn init_protocol<R: BufRead>(reader: &mut Reader<R>) -> Protocol {
39 for _ in 0..3 {
41 match reader.read_event_into(&mut Vec::new()) {
42 Ok(Event::Decl(_) | Event::DocType(_)) => {
43 continue;
44 }
45 Ok(Event::Start(bytes)) => {
46 assert!(bytes.name().into_inner() == b"protocol", "Missing protocol toplevel tag");
47 if let Some(attr) = bytes
48 .attributes()
49 .filter_map(|res| res.ok())
50 .find(|attr| attr.key.into_inner() == b"name")
51 {
52 return Protocol::new(decode_utf8_or_panic(attr.value.into_owned()));
53 } else {
54 panic!("Protocol must have a name");
55 }
56 }
57 _ => panic!("Ill-formed protocol file"),
58 }
59 }
60 panic!("Ill-formed protocol file");
61}
62
63fn parse_protocol<R: BufRead>(mut reader: Reader<R>) -> Protocol {
64 let mut protocol = init_protocol(&mut reader);
65
66 loop {
67 match reader.read_event_into(&mut Vec::new()) {
68 Ok(Event::Start(bytes)) => {
69 match bytes.name().into_inner() {
70 b"copyright" => {
71 let mut copyright = String::new();
73 loop {
74 match reader.read_event_into(&mut Vec::new()) {
75 Ok(Event::Text(text)) => {
76 if let Ok(text) = text.decode() {
77 copyright.push_str(&text);
78 }
79 }
80 Ok(Event::CData(cdata)) => {
81 if let Ok(cdata) = String::from_utf8(cdata.into_inner().into())
82 {
83 copyright.push_str(&cdata);
84 }
85 }
86 Ok(Event::GeneralRef(byte_ref)) => {
87 if let Ok(Some(c)) = byte_ref.resolve_char_ref() {
88 copyright.push(c);
89 } else if let Ok(content) = byte_ref.xml_content() {
90 if let Some(s) =
91 quick_xml::escape::resolve_xml_entity(&content)
92 {
93 copyright.push_str(s);
94 }
95 }
96 }
97 Ok(Event::End(bytes)) => {
98 assert!(
99 bytes.name().into_inner() == "copyright".as_bytes(),
100 "Ill-formed protocol file"
101 );
102 break;
103 }
104 e => {
105 panic!("Ill-formed protocol file: {e:?}");
106 }
107 }
108 }
109
110 protocol.copyright = Some(copyright)
111 }
112 b"interface" => {
113 protocol.interfaces.push(parse_interface(&mut reader, bytes.attributes()));
114 }
115 b"description" => {
116 protocol.description =
117 Some(parse_description(&mut reader, bytes.attributes()));
118 }
119 name => panic!(
120 "Ill-formed protocol file: unexpected token `{}` in protocol {}",
121 String::from_utf8_lossy(name),
122 protocol.name
123 ),
124 }
125 }
126 Ok(Event::End(bytes)) => {
127 let name = bytes.name().into_inner();
128 assert!(
129 name == b"protocol",
130 "Unexpected closing token `{}`",
131 String::from_utf8_lossy(name)
132 );
133 break;
134 }
135 Ok(Event::Comment(_)) => {}
137 e => panic!("Ill-formed protocol file: unexpected token {e:?}"),
138 }
139 }
140
141 protocol
142}
143
144fn parse_interface<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Interface {
145 let mut interface = Interface::new();
146 for attr in attrs.filter_map(|res| res.ok()) {
147 match attr.key.into_inner() {
148 b"name" => interface.name = decode_utf8_or_panic(attr.value.into_owned()),
149 b"version" => interface.version = parse_or_panic(&attr.value),
150 _ => {}
151 }
152 }
153
154 loop {
155 match reader.read_event_into(&mut Vec::new()) {
156 Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
157 b"description" => {
158 interface.description = Some(parse_description(reader, bytes.attributes()))
159 }
160 b"request" => interface.requests.push(parse_request(reader, bytes.attributes())),
161 b"event" => interface.events.push(parse_event(reader, bytes.attributes())),
162 b"enum" => interface.enums.push(parse_enum(reader, bytes.attributes())),
163 name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
164 },
165 Ok(Event::End(bytes)) if bytes.name().into_inner() == b"interface" => break,
166 _ => {}
167 }
168 }
169
170 interface
171}
172
173fn parse_description<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> (String, String) {
174 let mut summary = String::new();
175 for attr in attrs.filter_map(|res| res.ok()) {
176 if attr.key.into_inner() == b"summary" {
177 summary = String::from_utf8_lossy(&attr.value)
178 .split_whitespace()
179 .collect::<Vec<_>>()
180 .join(" ");
181 }
182 }
183
184 let mut description = String::new();
185 loop {
188 match reader.read_event_into(&mut Vec::new()) {
189 Ok(Event::Text(bytes)) => {
190 if !description.is_empty() {
191 description.push_str("\n\n");
192 }
193 description.push_str(&bytes.decode().unwrap_or_default())
194 }
195 Ok(Event::End(bytes)) if bytes.name().into_inner() == b"description" => break,
196 Ok(Event::Comment(_)) => {}
197 e => panic!("Ill-formed protocol file: {e:?}"),
198 }
199 }
200
201 (summary, description)
202}
203
204fn parse_request<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Message {
205 let mut request = Message::new();
206 for attr in attrs.filter_map(|res| res.ok()) {
207 match attr.key.into_inner() {
208 b"name" => request.name = decode_utf8_or_panic(attr.value.into_owned()),
209 b"type" => request.typ = Some(parse_type(&attr.value)),
210 b"since" => request.since = parse_or_panic(&attr.value),
211 _ => {}
212 }
213 }
214
215 loop {
216 match reader.read_event_into(&mut Vec::new()) {
217 Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
218 b"description" => {
219 request.description = Some(parse_description(reader, bytes.attributes()))
220 }
221 b"arg" => request.args.push(parse_arg(reader, bytes.attributes())),
222 name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
223 },
224 Ok(Event::End(bytes)) if bytes.name().into_inner() == b"request" => break,
225 _ => {}
226 }
227 }
228
229 request
230}
231
232fn parse_enum<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Enum {
233 let mut enu = Enum::new();
234 for attr in attrs.filter_map(|res| res.ok()) {
235 match attr.key.into_inner() {
236 b"name" => enu.name = decode_utf8_or_panic(attr.value.into_owned()),
237 b"since" => enu.since = parse_or_panic(&attr.value),
238 b"bitfield" => {
239 if &attr.value[..] == b"true" {
240 enu.bitfield = true
241 }
242 }
243 _ => {}
244 }
245 }
246
247 loop {
248 match reader.read_event_into(&mut Vec::new()) {
249 Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
250 b"description" => {
251 enu.description = Some(parse_description(reader, bytes.attributes()))
252 }
253 b"entry" => enu.entries.push(parse_entry(reader, bytes.attributes())),
254 name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
255 },
256 Ok(Event::End(bytes)) if bytes.name().into_inner() == b"enum" => break,
257 _ => {}
258 }
259 }
260
261 enu
262}
263
264fn parse_event<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Message {
265 let mut event = Message::new();
266 for attr in attrs.filter_map(|res| res.ok()) {
267 match attr.key.into_inner() {
268 b"name" => event.name = decode_utf8_or_panic(attr.value.into_owned()),
269 b"type" => event.typ = Some(parse_type(&attr.value)),
270 b"since" => event.since = parse_or_panic(&attr.value),
271 _ => {}
272 }
273 }
274
275 loop {
276 match reader.read_event_into(&mut Vec::new()) {
277 Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
278 b"description" => {
279 event.description = Some(parse_description(reader, bytes.attributes()))
280 }
281 b"arg" => event.args.push(parse_arg(reader, bytes.attributes())),
282 name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
283 },
284 Ok(Event::End(bytes)) if bytes.name().into_inner() == b"event" => break,
285 _ => {}
286 }
287 }
288
289 event
290}
291
292fn parse_arg<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Arg {
293 let mut arg = Arg::new();
294 for attr in attrs.filter_map(|res| res.ok()) {
295 match attr.key.into_inner() {
296 b"name" => arg.name = decode_utf8_or_panic(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 }
306 b"interface" => arg.interface = Some(parse_or_panic(&attr.value)),
307 b"allow-null" => {
308 if &*attr.value == b"true" {
309 arg.allow_null = true
310 }
311 }
312 b"enum" => arg.enum_ = Some(decode_utf8_or_panic(attr.value.into_owned())),
313 _ => {}
314 }
315 }
316
317 loop {
318 match reader.read_event_into(&mut Vec::new()) {
319 Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
320 b"description" => {
321 arg.description = Some(parse_description(reader, bytes.attributes()))
322 }
323 name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
324 },
325 Ok(Event::End(bytes)) if bytes.name().into_inner() == b"arg" => break,
326 _ => {}
327 }
328 }
329
330 arg
331}
332
333fn parse_type(txt: &[u8]) -> Type {
334 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 => panic!("Unexpected type: {}", String::from_utf8_lossy(e)),
345 }
346}
347
348fn parse_entry<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Entry {
349 let mut entry = Entry::new();
350 for attr in attrs.filter_map(|res| res.ok()) {
351 match attr.key.into_inner() {
352 b"name" => entry.name = decode_utf8_or_panic(attr.value.into_owned()),
353 b"value" => {
354 entry.value = if attr.value.starts_with(b"0x") {
355 if let Some(val) = std::str::from_utf8(&attr.value[2..])
356 .ok()
357 .and_then(|s| u32::from_str_radix(s, 16).ok())
358 {
359 val
360 } else {
361 panic!("Invalid number: {}", String::from_utf8_lossy(&attr.value))
362 }
363 } else {
364 parse_or_panic(&attr.value)
365 };
366 }
367 b"since" => entry.since = parse_or_panic(&attr.value),
368 b"summary" => {
369 entry.summary = Some(
370 String::from_utf8_lossy(&attr.value)
371 .split_whitespace()
372 .collect::<Vec<_>>()
373 .join(" "),
374 )
375 }
376 _ => {}
377 }
378 }
379
380 loop {
381 match reader.read_event_into(&mut Vec::new()) {
382 Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
383 b"description" => {
384 entry.description = Some(parse_description(reader, bytes.attributes()))
385 }
386 name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
387 },
388 Ok(Event::End(bytes)) if bytes.name().into_inner() == b"entry" => break,
389 _ => {}
390 }
391 }
392
393 entry
394}
395
396#[cfg(test)]
397mod tests {
398 #[test]
399 fn xml_parse() {
400 let protocol_file =
401 std::fs::File::open("./tests/scanner_assets/test-protocol.xml").unwrap();
402 let _ = crate::parse::parse(protocol_file);
403 }
404
405 #[test]
406 fn headerless_xml_parse() {
407 let protocol_file =
408 std::fs::File::open("./tests/scanner_assets/test-headerless-protocol.xml").unwrap();
409 let _ = crate::parse::parse(protocol_file);
410 }
411}