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> = Reader::from_reader(BufReader::new(inner:stream));
31 reader.trim_text(true).expand_empty_elements(val:true);
32 // Skip first <?xml ... ?> event
33 let _ = reader.read_event_into(&mut Vec::new());
34 parse_protocol(reader)
35}
36
37fn decode_utf8_or_panic(txt: Vec<u8>) -> String {
38 match String::from_utf8(vec:txt) {
39 Ok(txt: String) => txt,
40 Err(e: FromUtf8Error) => 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: &str| val.parse().ok()) {
46 Some(version: T) => 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 parse_protocol<R: BufRead>(mut reader: Reader<R>) -> Protocol {
56 let mut protocol = extract_from!(
57 reader => Event::Start(bytes) => {
58 assert!(bytes.name().into_inner() == b"protocol", "Missing protocol toplevel tag");
59 if let Some(attr) = bytes.attributes().filter_map(|res| res.ok()).find(|attr| attr.key.into_inner() == b"name") {
60 Protocol::new(decode_utf8_or_panic(attr.value.into_owned()))
61 } else {
62 panic!("Protocol must have a name");
63 }
64 }
65 );
66
67 loop {
68 match reader.read_event_into(&mut Vec::new()) {
69 Ok(Event::Start(bytes)) => {
70 match bytes.name().into_inner() {
71 b"copyright" => {
72 // parse the copyright
73 let copyright = match reader.read_event_into(&mut Vec::new()) {
74 Ok(Event::Text(copyright)) => {
75 copyright.unescape().ok().map(|x| x.to_string())
76 }
77 Ok(Event::CData(copyright)) => {
78 String::from_utf8(copyright.into_inner().into()).ok()
79 }
80 e => panic!("Ill-formed protocol file: {:?}", e),
81 };
82
83 extract_end_tag!(reader => "copyright");
84 protocol.copyright = copyright
85 }
86 b"interface" => {
87 protocol.interfaces.push(parse_interface(&mut reader, bytes.attributes()));
88 }
89 b"description" => {
90 protocol.description =
91 Some(parse_description(&mut reader, bytes.attributes()));
92 }
93 name => panic!(
94 "Ill-formed protocol file: unexpected token `{}` in protocol {}",
95 String::from_utf8_lossy(name),
96 protocol.name
97 ),
98 }
99 }
100 Ok(Event::End(bytes)) => {
101 let name = bytes.name().into_inner();
102 assert!(
103 name == b"protocol",
104 "Unexpected closing token `{}`",
105 String::from_utf8_lossy(name)
106 );
107 break;
108 }
109 // ignore comments
110 Ok(Event::Comment(_)) => {}
111 e => panic!("Ill-formed protocol file: unexpected token {:?}", e),
112 }
113 }
114
115 protocol
116}
117
118fn parse_interface<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Interface {
119 let mut interface = Interface::new();
120 for attr in attrs.filter_map(|res| res.ok()) {
121 match attr.key.into_inner() {
122 b"name" => interface.name = decode_utf8_or_panic(attr.value.into_owned()),
123 b"version" => interface.version = parse_or_panic(&attr.value),
124 _ => {}
125 }
126 }
127
128 loop {
129 match reader.read_event_into(&mut Vec::new()) {
130 Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
131 b"description" => {
132 interface.description = Some(parse_description(reader, bytes.attributes()))
133 }
134 b"request" => interface.requests.push(parse_request(reader, bytes.attributes())),
135 b"event" => interface.events.push(parse_event(reader, bytes.attributes())),
136 b"enum" => interface.enums.push(parse_enum(reader, bytes.attributes())),
137 name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
138 },
139 Ok(Event::End(bytes)) if bytes.name().into_inner() == b"interface" => break,
140 _ => {}
141 }
142 }
143
144 interface
145}
146
147fn parse_description<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> (String, String) {
148 let mut summary = String::new();
149 for attr in attrs.filter_map(|res| res.ok()) {
150 if attr.key.into_inner() == b"summary" {
151 summary = String::from_utf8_lossy(&attr.value)
152 .split_whitespace()
153 .collect::<Vec<_>>()
154 .join(" ");
155 }
156 }
157
158 let mut description = String::new();
159 // Some protocols have comments inside their descriptions, so we need to parse them in a loop and
160 // concatenate the parts into a single block of text
161 loop {
162 match reader.read_event_into(&mut Vec::new()) {
163 Ok(Event::Text(bytes)) => {
164 if !description.is_empty() {
165 description.push_str("\n\n");
166 }
167 description.push_str(&bytes.unescape().unwrap_or_default())
168 }
169 Ok(Event::End(bytes)) if bytes.name().into_inner() == b"description" => break,
170 Ok(Event::Comment(_)) => {}
171 e => panic!("Ill-formed protocol file: {:?}", e),
172 }
173 }
174
175 (summary, description)
176}
177
178fn parse_request<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Message {
179 let mut request = Message::new();
180 for attr in attrs.filter_map(|res| res.ok()) {
181 match attr.key.into_inner() {
182 b"name" => request.name = decode_utf8_or_panic(attr.value.into_owned()),
183 b"type" => request.typ = Some(parse_type(&attr.value)),
184 b"since" => request.since = parse_or_panic(&attr.value),
185 _ => {}
186 }
187 }
188
189 loop {
190 match reader.read_event_into(&mut Vec::new()) {
191 Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
192 b"description" => {
193 request.description = Some(parse_description(reader, bytes.attributes()))
194 }
195 b"arg" => request.args.push(parse_arg(reader, bytes.attributes())),
196 name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
197 },
198 Ok(Event::End(bytes)) if bytes.name().into_inner() == b"request" => break,
199 _ => {}
200 }
201 }
202
203 request
204}
205
206fn parse_enum<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Enum {
207 let mut enu = Enum::new();
208 for attr in attrs.filter_map(|res| res.ok()) {
209 match attr.key.into_inner() {
210 b"name" => enu.name = decode_utf8_or_panic(attr.value.into_owned()),
211 b"since" => enu.since = parse_or_panic(&attr.value),
212 b"bitfield" => {
213 if &attr.value[..] == b"true" {
214 enu.bitfield = true
215 }
216 }
217 _ => {}
218 }
219 }
220
221 loop {
222 match reader.read_event_into(&mut Vec::new()) {
223 Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
224 b"description" => {
225 enu.description = Some(parse_description(reader, bytes.attributes()))
226 }
227 b"entry" => enu.entries.push(parse_entry(reader, bytes.attributes())),
228 name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
229 },
230 Ok(Event::End(bytes)) if bytes.name().into_inner() == b"enum" => break,
231 _ => {}
232 }
233 }
234
235 enu
236}
237
238fn parse_event<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Message {
239 let mut event = Message::new();
240 for attr in attrs.filter_map(|res| res.ok()) {
241 match attr.key.into_inner() {
242 b"name" => event.name = decode_utf8_or_panic(attr.value.into_owned()),
243 b"type" => event.typ = Some(parse_type(&attr.value)),
244 b"since" => event.since = parse_or_panic(&attr.value),
245 _ => {}
246 }
247 }
248
249 loop {
250 match reader.read_event_into(&mut Vec::new()) {
251 Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
252 b"description" => {
253 event.description = Some(parse_description(reader, bytes.attributes()))
254 }
255 b"arg" => event.args.push(parse_arg(reader, bytes.attributes())),
256 name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
257 },
258 Ok(Event::End(bytes)) if bytes.name().into_inner() == b"event" => break,
259 _ => {}
260 }
261 }
262
263 event
264}
265
266fn parse_arg<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Arg {
267 let mut arg = Arg::new();
268 for attr in attrs.filter_map(|res| res.ok()) {
269 match attr.key.into_inner() {
270 b"name" => arg.name = decode_utf8_or_panic(attr.value.into_owned()),
271 b"type" => arg.typ = parse_type(&attr.value),
272 b"summary" => {
273 arg.summary = Some(
274 String::from_utf8_lossy(&attr.value)
275 .split_whitespace()
276 .collect::<Vec<_>>()
277 .join(" "),
278 )
279 }
280 b"interface" => arg.interface = Some(parse_or_panic(&attr.value)),
281 b"allow-null" => {
282 if &*attr.value == b"true" {
283 arg.allow_null = true
284 }
285 }
286 b"enum" => arg.enum_ = Some(decode_utf8_or_panic(attr.value.into_owned())),
287 _ => {}
288 }
289 }
290
291 loop {
292 match reader.read_event_into(&mut Vec::new()) {
293 Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
294 b"description" => {
295 arg.description = Some(parse_description(reader, bytes.attributes()))
296 }
297 name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
298 },
299 Ok(Event::End(bytes)) if bytes.name().into_inner() == b"arg" => break,
300 _ => {}
301 }
302 }
303
304 arg
305}
306
307fn parse_type(txt: &[u8]) -> Type {
308 match txt {
309 b"int" => Type::Int,
310 b"uint" => Type::Uint,
311 b"fixed" => Type::Fixed,
312 b"string" => Type::String,
313 b"object" => Type::Object,
314 b"new_id" => Type::NewId,
315 b"array" => Type::Array,
316 b"fd" => Type::Fd,
317 b"destructor" => Type::Destructor,
318 e: &[u8] => panic!("Unexpected type: {}", String::from_utf8_lossy(e)),
319 }
320}
321
322fn parse_entry<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Entry {
323 let mut entry = Entry::new();
324 for attr in attrs.filter_map(|res| res.ok()) {
325 match attr.key.into_inner() {
326 b"name" => entry.name = decode_utf8_or_panic(attr.value.into_owned()),
327 b"value" => {
328 entry.value = if attr.value.starts_with(b"0x") {
329 if let Some(val) = std::str::from_utf8(&attr.value[2..])
330 .ok()
331 .and_then(|s| u32::from_str_radix(s, 16).ok())
332 {
333 val
334 } else {
335 panic!("Invalid number: {}", String::from_utf8_lossy(&attr.value))
336 }
337 } else {
338 parse_or_panic(&attr.value)
339 };
340 }
341 b"since" => entry.since = parse_or_panic(&attr.value),
342 b"summary" => {
343 entry.summary = Some(
344 String::from_utf8_lossy(&attr.value)
345 .split_whitespace()
346 .collect::<Vec<_>>()
347 .join(" "),
348 )
349 }
350 _ => {}
351 }
352 }
353
354 loop {
355 match reader.read_event_into(&mut Vec::new()) {
356 Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
357 b"description" => {
358 entry.description = Some(parse_description(reader, bytes.attributes()))
359 }
360 name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
361 },
362 Ok(Event::End(bytes)) if bytes.name().into_inner() == b"entry" => break,
363 _ => {}
364 }
365 }
366
367 entry
368}
369