1 | use super::protocol::*; |
2 | use std::{ |
3 | io::{BufRead, BufReader, Read}, |
4 | str::FromStr, |
5 | }; |
6 | |
7 | use quick_xml::{ |
8 | events::{attributes::Attributes, Event}, |
9 | Reader, |
10 | }; |
11 | |
12 | macro_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 | |
21 | macro_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 | |
29 | pub 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 | |
37 | fn 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 | |
44 | fn 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 | |
55 | fn 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 | |
118 | fn 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 | |
147 | fn 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 | |
178 | fn 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 | |
206 | fn 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 | |
238 | fn 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 | |
266 | fn 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 | |
307 | fn 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 | |
322 | fn 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 | |