blob: a83fee1ba25d5b0b7a9be5b2d2baf59580d9bca2 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use xml::reader::{EventReader, XmlEvent};
use std::error;
use std::fmt;
use std::io::Read;
use std::str::{FromStr, ParseBoolError};
#[derive(Debug, PartialEq)]
pub enum ArgKind {
Int,
Uint,
Fixed,
Object,
NewId,
String,
Array,
Fd,
}
impl ArgKind {
fn from_str(s: &str) -> Option<ArgKind> {
match s {
"int" => Some(ArgKind::Int),
"uint" => Some(ArgKind::Uint),
"fixed" => Some(ArgKind::Fixed),
"string" => Some(ArgKind::String),
"object" => Some(ArgKind::Object),
"new_id" => Some(ArgKind::NewId),
"array" => Some(ArgKind::Array),
"fd" => Some(ArgKind::Fd),
_ => None,
}
}
}
#[derive(Debug)]
pub enum ParseElement {
Protocol {
name: String,
},
Copyright,
Interface {
name: String,
version: u32,
},
Description {
summary: String,
},
Request {
name: String,
since: u32,
request_type: Option<String>,
},
Event {
name: String,
since: u32,
},
Arg {
name: String,
kind: ArgKind,
summary: Option<String>,
interface: Option<String>,
nullable: bool,
enum_type: Option<String>,
},
Enum {
name: String,
since: u32,
bitfield: bool,
},
EnumEntry {
name: String,
value: i64,
summary: Option<String>,
since: u32,
},
}
#[derive(Debug)]
pub struct ParseNode {
pub element: ParseElement,
pub body: Option<String>,
pub children: Vec<ParseNode>,
}
pub struct Parser<R: Read> {
xml: xml::reader::EventReader<R>,
}
#[derive(Debug, Clone)]
pub struct ParseError {
msg: String,
}
impl ParseError {
fn new(msg: String) -> Self {
ParseError { msg }
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.msg)
}
}
impl error::Error for ParseError {
fn description(&self) -> &str {
&self.msg
}
fn cause(&self) -> Option<&error::Error> {
None
}
}
impl From<ParseBoolError> for ParseError {
fn from(e: ParseBoolError) -> Self {
ParseError::new(format!("Failed to parse boolean {}", e))
}
}
pub type ParseResult<T> = Result<T, ParseError>;
/// The default rust numeric parse routines do not handle '0x..' constants.
/// This just maps 0x to a radix of 16.
fn parse_int<T: FromStr + num::Num>(s: &str) -> ParseResult<T> {
if s.starts_with("0x") {
T::from_str_radix(&s[2..], 16)
.map_err(|_| ParseError::new(format!("Failed to parse int {}", s)))
} else {
s.parse::<T>()
.map_err(|_| ParseError::new(format!("Failed to parse int {}", s)))
}
}
trait XmlAttr
where
Self: Sized,
{
fn default() -> Self;
fn from_xml_attr(attr: xml::attribute::OwnedAttribute) -> ParseResult<Self>;
}
impl<T> XmlAttr for Option<T>
where
T: XmlAttr,
{
fn default() -> Self {
None
}
fn from_xml_attr(attr: xml::attribute::OwnedAttribute) -> ParseResult<Self> {
T::from_xml_attr(attr).map(|t| Some(t))
}
}
macro_rules! impl_xml_attr {
($type:ty, $default:expr, $attr:ident => $from_attr:expr) => {
impl XmlAttr for $type {
fn default() -> Self {
$default
}
fn from_xml_attr($attr: xml::attribute::OwnedAttribute) -> ParseResult<Self> {
$from_attr
}
}
};
}
impl_xml_attr!(u32, 0, attr => parse_int::<Self>(&attr.value));
impl_xml_attr!(i64, 0, attr => parse_int::<Self>(&attr.value));
impl_xml_attr!(bool, false, attr => Ok(attr.value.parse::<bool>()?));
impl_xml_attr!(String, "".to_owned(), attr => Ok(attr.value));
impl_xml_attr!(Option<ArgKind>, None, attr => Ok(ArgKind::from_str(&attr.value)));
/// Simplifies binding a set of XML attributes to an expression.
///
///
/// Ex:
///
/// // Reads the XML attribute with the name 'attr1' and stores the value into
/// // |var1|. If |attrs| does not contain 'attr1' then |var1| will be None.
/// map_xml_attrs!(attrs, ("attr1" => var1: Option<String>) => {
/// println!("Found attr1 == {}", var1)
/// });
///
/// There must be a corresponding |XmlAttr| trait implementation for any type
/// that appears in the argument list.
macro_rules! map_xml_attrs {
($attributes:expr, ($($xml_name:expr => $field_name:ident : $field_type:ty),*) => { $body:expr }) => {
{
$(
let mut $field_name : $field_type = XmlAttr::default();
)*
for attr in $attributes {
match attr.name.local_name.as_ref() {
$(
$xml_name => $field_name = XmlAttr::from_xml_attr(attr)?,
)*
_ => {
return Err(ParseError::new(format!(
"Unsupported attribute {}",
attr.name.local_name
)));
}
}
}
$body
}
}
}
impl<R: Read> Parser<R> {
/// Reads the entire XML document, returning the root node of the parse
/// tree.
pub fn read_document(&mut self) -> ParseResult<ParseNode> {
if let Ok(XmlEvent::StartDocument { .. }) = self.xml.next() {
self.read_node()
} else {
Err(ParseError::new("Missing StartDocument".to_owned()))
}
}
fn read_node(&mut self) -> ParseResult<ParseNode> {
match self.xml.next() {
Ok(XmlEvent::StartElement {
name, attributes, ..
}) => {
let element = self.element_from_xml(name.local_name.as_ref(), attributes)?;
self.populate_node(element, name.local_name.as_ref())
}
node => Err(ParseError::new(format!("Not Implemented {:?}", node))),
}
}
fn populate_node(&mut self, element: ParseElement, xml_name: &str) -> ParseResult<ParseNode> {
let mut children: Vec<ParseNode> = Vec::new();
let mut body: Option<String> = None;
loop {
match self.xml.next() {
Ok(XmlEvent::EndElement { name }) => {
if name.local_name == xml_name {
return Ok(ParseNode {
element,
children,
body,
});
}
return Err(ParseError::new("Unexpected EndElement".to_owned()));
}
Ok(XmlEvent::StartElement {
name, attributes, ..
}) => {
let element = self.element_from_xml(name.local_name.as_ref(), attributes)?;
let node = self.populate_node(element, name.local_name.as_ref())?;
children.push(node);
}
Ok(XmlEvent::CData(text)) | Ok(XmlEvent::Characters(text)) => {
body = Some(text);
}
Ok(XmlEvent::Whitespace(_)) | Ok(XmlEvent::Comment(_)) => continue,
event => {
return Err(ParseError::new(format!("Unhandled event {:?}", event)));
}
}
}
}
fn element_from_xml(
&self, element_name: &str, attributes: Vec<xml::attribute::OwnedAttribute>,
) -> ParseResult<ParseElement> {
match element_name {
"protocol" => map_xml_attrs!(attributes,
("name" => name: Option<String>) => {
Ok(ParseElement::Protocol {
name: name
.ok_or_else(|| ParseError::new("Missing 'name' attribute".to_owned()))?,
})
}),
"copyright" => Ok(ParseElement::Copyright),
"interface" => map_xml_attrs!(attributes,
("name" => name: Option<String>,
"version" => version: u32) => {
Ok(ParseElement::Interface {
name: name
.ok_or_else(|| ParseError::new("Missing 'name' attribute".to_owned()))?,
version,
})
}),
"request" => map_xml_attrs!(attributes,
("name" => name: Option<String>,
"type" => request_type: Option<String>,
"since" => since: u32) => {
Ok(ParseElement::Request {
name: name
.ok_or_else(|| ParseError::new("Missing 'name' attribute".to_owned()))?,
request_type,
since,
})
}),
"event" => map_xml_attrs!(attributes,
("name" => name: Option<String>,
"since" => since: u32) => {
Ok(ParseElement::Event {
name: name
.ok_or_else(|| ParseError::new("Missing 'name' attribute".to_owned()))?,
since,
})
}),
"enum" => map_xml_attrs!(attributes,
("name" => name: Option<String>,
"since" => since: u32,
"bitfield" => bitfield: bool) => {
Ok(ParseElement::Enum {
name: name
.ok_or_else(|| ParseError::new("Missing 'name' attribute".to_owned()))?,
since,
bitfield,
})
}),
"entry" => map_xml_attrs!(attributes,
("name" => name: Option<String>,
"value" => value: Option<i64>,
"since" => since: u32,
"summary" => summary: Option<String>) => {
Ok(ParseElement::EnumEntry {
name: name
.ok_or_else(|| ParseError::new("Missing 'name' attribute".to_owned()))?,
value: value
.ok_or_else(|| ParseError::new("Missing 'value' attribute".to_owned()))?,
since,
summary,
})
}),
"arg" => map_xml_attrs!(attributes,
("name" => name: Option<String>,
"type" => kind: Option<ArgKind>,
"summary" => summary: Option<String>,
"interface" => interface: Option<String>,
"allow-null" => nullable: bool,
"enum" => enum_type: Option<String>) => {
Ok(ParseElement::Arg {
name: name.ok_or_else(|| {
ParseError::new("Missing 'name' attributue on argument".to_owned())
})?,
kind: kind.ok_or_else(|| {
ParseError::new("Missing 'type' attributue on argument".to_owned())
})?,
summary,
interface,
nullable,
enum_type,
})
}),
"description" => map_xml_attrs!(attributes,
("summary" => summary: Option<String>) => {
Ok(ParseElement::Description {
summary: summary.ok_or_else(|| {
ParseError::new("Missing 'summary' attributue on argument".to_owned())
})?,
})
}),
tag => Err(ParseError::new(format!("Tag not implemented {}", tag))),
}
}
}
impl<R: Read> Parser<R> {
pub fn new(source: R) -> Parser<R> {
Parser {
xml: EventReader::new(source),
}
}
}
impl<'a> Parser<&'a [u8]> {
pub fn from_str(protocol: &str) -> Parser<&[u8]> {
Parser {
xml: EventReader::from_str(protocol),
}
}
}