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