use crate::protocol::*; use std::io::Read; use xml::attribute::OwnedAttribute; use xml::reader::ParserConfig; use xml::reader::XmlEvent; use xml::EventReader; macro_rules! extract_from( ($it: expr => $pattern: pat => $result: expr) => ( match $it.next() { Ok($pattern) => { $result }, e => panic!("Ill-formed protocol file: {:?}", e) } ) ); macro_rules! extract_end_tag( ($it: expr => $tag: expr) => ( extract_from!($it => XmlEvent::EndElement { name } => { assert!(name.local_name == $tag, "Ill-formed protocol file"); }); ) ); pub fn parse_stream(stream: S) -> Protocol { let mut reader = EventReader::new_with_config(stream, ParserConfig::new().trim_whitespace(true)); reader.next().expect("Could not read from event reader"); parse_protocol(reader) } fn parse_protocol(mut reader: EventReader) -> Protocol { let mut protocol = extract_from!( reader => XmlEvent::StartElement { name, attributes, .. } => { assert!(name.local_name == "protocol", "Missing protocol toplevel tag"); assert!(attributes[0].name.local_name == "name", "Protocol must have a name"); Protocol::new(attributes[0].value.clone()) } ); loop { match reader.next() { Ok(XmlEvent::StartElement { name, attributes, .. }) => { match &name.local_name[..] { "copyright" => { // parse the copyright let copyright = match reader.next() { Ok(XmlEvent::Characters(copyright)) | Ok(XmlEvent::CData(copyright)) => copyright, e => panic!("Ill-formed protocol file: {:?}", e), }; extract_end_tag!(reader => "copyright"); protocol.copyright = Some(copyright); } "interface" => { protocol.interfaces.push(parse_interface(&mut reader, attributes)); } "description" => { protocol.description = Some(parse_description(&mut reader, attributes)); } _ => panic!( "Ill-formed protocol file: unexpected token `{}` in protocol {}", name.local_name, protocol.name ), } } Ok(XmlEvent::EndElement { name }) => { assert!( name.local_name == "protocol", "Unexpected closing token `{}`", name.local_name ); break; } e => panic!("Ill-formed protocol file: {:?}", e), } } protocol } fn parse_interface(reader: &mut EventReader, attrs: Vec) -> Interface { let mut interface = Interface::new(); for attr in attrs { match &attr.name.local_name[..] { "name" => interface.name = attr.value, "version" => interface.version = attr.value.parse().unwrap(), _ => {} } } loop { match reader.next() { Ok(XmlEvent::StartElement { name, attributes, .. }) => match &name.local_name[..] { "description" => { interface.description = Some(parse_description(reader, attributes)) } "request" => interface.requests.push(parse_request(reader, attributes)), "event" => interface.events.push(parse_event(reader, attributes)), "enum" => interface.enums.push(parse_enum(reader, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), }, Ok(XmlEvent::EndElement { ref name }) if name.local_name == "interface" => break, _ => {} } } interface } fn parse_description( reader: &mut EventReader, attrs: Vec, ) -> (String, String) { let mut summary = String::new(); for attr in attrs { if &attr.name.local_name[..] == "summary" { summary = attr.value.split_whitespace().collect::>().join(" "); } } let description = match reader.next() { Ok(XmlEvent::Characters(txt)) => { extract_end_tag!(reader => "description"); txt } Ok(XmlEvent::EndElement { ref name }) if name.local_name == "description" => String::new(), e => panic!("Ill-formed protocol file: {:?}", e), }; (summary, description) } fn parse_request(reader: &mut EventReader, attrs: Vec) -> Message { let mut request = Message::new(); for attr in attrs { match &attr.name.local_name[..] { "name" => request.name = attr.value, "type" => request.typ = Some(parse_type(&attr.value)), "since" => request.since = attr.value.parse().unwrap(), _ => {} } } loop { match reader.next() { Ok(XmlEvent::StartElement { name, attributes, .. }) => match &name.local_name[..] { "description" => request.description = Some(parse_description(reader, attributes)), "arg" => request.args.push(parse_arg(reader, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), }, Ok(XmlEvent::EndElement { ref name }) if name.local_name == "request" => break, _ => {} } } request } fn parse_enum(reader: &mut EventReader, attrs: Vec) -> Enum { let mut enu = Enum::new(); for attr in attrs { match &attr.name.local_name[..] { "name" => enu.name = attr.value, "since" => enu.since = attr.value.parse().unwrap(), "bitfield" => { if &attr.value[..] == "true" { enu.bitfield = true } } _ => {} } } loop { match reader.next() { Ok(XmlEvent::StartElement { name, attributes, .. }) => match &name.local_name[..] { "description" => enu.description = Some(parse_description(reader, attributes)), "entry" => enu.entries.push(parse_entry(reader, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), }, Ok(XmlEvent::EndElement { ref name }) if name.local_name == "enum" => break, _ => {} } } enu } fn parse_event(reader: &mut EventReader, attrs: Vec) -> Message { let mut event = Message::new(); for attr in attrs { match &attr.name.local_name[..] { "name" => event.name = attr.value, "type" => event.typ = Some(parse_type(&attr.value)), "since" => event.since = attr.value.parse().unwrap(), _ => {} } } loop { match reader.next() { Ok(XmlEvent::StartElement { name, attributes, .. }) => match &name.local_name[..] { "description" => event.description = Some(parse_description(reader, attributes)), "arg" => event.args.push(parse_arg(reader, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), }, Ok(XmlEvent::EndElement { ref name }) if name.local_name == "event" => break, _ => {} } } event } fn parse_arg(reader: &mut EventReader, attrs: Vec) -> Arg { let mut arg = Arg::new(); for attr in attrs { match &attr.name.local_name[..] { "name" => arg.name = attr.value, "type" => arg.typ = parse_type(&attr.value), "summary" => { arg.summary = Some(attr.value.split_whitespace().collect::>().join(" ")) } "interface" => arg.interface = Some(attr.value), "allow-null" => { if attr.value == "true" { arg.allow_null = true } } "enum" => arg.enum_ = Some(attr.value), _ => {} } } loop { match reader.next() { Ok(XmlEvent::StartElement { name, attributes, .. }) => match &name.local_name[..] { "description" => arg.description = Some(parse_description(reader, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), }, Ok(XmlEvent::EndElement { ref name }) if name.local_name == "arg" => break, _ => {} } } arg } fn parse_type(txt: &str) -> Type { match txt { "int" => Type::Int, "uint" => Type::Uint, "fixed" => Type::Fixed, "string" => Type::String, "object" => Type::Object, "new_id" => Type::NewId, "array" => Type::Array, "fd" => Type::Fd, "destructor" => Type::Destructor, e => panic!("Unexpected type: {}", e), } } fn parse_entry(reader: &mut EventReader, attrs: Vec) -> Entry { let mut entry = Entry::new(); for attr in attrs { match &attr.name.local_name[..] { "name" => entry.name = attr.value, "value" => { entry.value = if attr.value.starts_with("0x") { u32::from_str_radix(&attr.value[2..], 16).unwrap() } else { attr.value.parse().unwrap() }; } "since" => entry.since = attr.value.parse().unwrap(), "summary" => { entry.summary = Some(attr.value.split_whitespace().collect::>().join(" ")) } _ => {} } } loop { match reader.next() { Ok(XmlEvent::StartElement { name, attributes, .. }) => match &name.local_name[..] { "description" => entry.description = Some(parse_description(reader, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), }, Ok(XmlEvent::EndElement { ref name }) if name.local_name == "entry" => break, _ => {} } } entry }