blob: 81a581d760cb913c28b253216e83fd02f0907a99 [file] [log] [blame] [edit]
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<S: Read>(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<R: Read>(mut reader: EventReader<R>) -> 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<R: Read>(reader: &mut EventReader<R>, attrs: Vec<OwnedAttribute>) -> 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<R: Read>(
reader: &mut EventReader<R>,
attrs: Vec<OwnedAttribute>,
) -> (String, String) {
let mut summary = String::new();
for attr in attrs {
if &attr.name.local_name[..] == "summary" {
summary = attr.value.split_whitespace().collect::<Vec<_>>().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<R: Read>(reader: &mut EventReader<R>, attrs: Vec<OwnedAttribute>) -> 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<R: Read>(reader: &mut EventReader<R>, attrs: Vec<OwnedAttribute>) -> 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<R: Read>(reader: &mut EventReader<R>, attrs: Vec<OwnedAttribute>) -> 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<R: Read>(reader: &mut EventReader<R>, attrs: Vec<OwnedAttribute>) -> 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::<Vec<_>>().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<R: Read>(reader: &mut EventReader<R>, attrs: Vec<OwnedAttribute>) -> 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::<Vec<_>>().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
}