#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::{
convert::Infallible,
io::Read,
num::ParseIntError,
str::{FromStr, ParseBoolError},
};
use thiserror::Error;
use xml::{
attribute::OwnedAttribute,
reader::{
EventReader,
ParserConfig,
XmlEvent::{self, *},
},
};
type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug)]
pub enum Error {
#[error("no protocol in file")]
NoProtocol,
#[error("xml parsing failed")]
XmlParse(#[from] xml::reader::Error),
#[error("unexpected xml event: {0:?}")]
UnexpectedXmlEvent(XmlEvent),
#[error("unexpected xml element: {0}")]
UnexpectedXmlElement(String),
#[error("parsing int failed")]
ParseInt(#[from] ParseIntError),
#[error("parsing bool failed")]
ParseBool(#[from] ParseBoolError),
#[error("missing required attribute: {0}")]
MissingRequiredAttribute(String),
#[error("invalid argument kind: {0}")]
InvalidArgKind(String),
}
impl From<Infallible> for Error {
fn from(i: Infallible) -> Self {
match i {}
}
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Protocol {
pub name: String,
pub copyright: String,
pub description: Option<Description>,
pub interfaces: Vec<Interface>,
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Interface {
pub name: String,
pub version: u32,
pub description: Option<Description>,
pub requests: Vec<Request>,
pub events: Vec<Event>,
pub enums: Vec<Enum>,
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Request {
pub name: String,
pub destructor: bool,
pub since: u32,
pub description: Option<Description>,
pub args: Vec<Arg>,
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Event {
pub name: String,
pub since: u32,
pub description: Option<Description>,
pub args: Vec<Arg>,
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Arg {
pub name: String,
pub kind: ArgKind,
pub summary: Option<String>,
pub interface: Option<String>,
pub allow_null: bool,
pub enumeration: Option<String>,
pub description: Option<Description>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ArgKind {
NewId,
Int,
Uint,
Fixed,
String,
Object,
Array,
Fd,
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Enum {
pub name: String,
pub since: u32,
pub bitfield: bool,
pub description: Option<Description>,
pub entries: Vec<Entry>,
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Entry {
pub name: String,
pub value: u32,
pub summary: Option<String>,
pub since: u32,
pub description: Option<Description>,
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Description {
pub summary: String,
pub body: String,
}
impl Default for ArgKind {
fn default() -> Self {
Self::NewId
}
}
impl FromStr for ArgKind {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(match s {
"new_id" => ArgKind::NewId,
"int" => ArgKind::Int,
"uint" => ArgKind::Uint,
"fixed" => ArgKind::Fixed,
"string" => ArgKind::String,
"object" => ArgKind::Object,
"array" => ArgKind::Array,
"fd" => ArgKind::Fd,
_ => return Err(Error::InvalidArgKind(s.into())),
})
}
}
struct ParseContext<R: Read> {
event_reader: EventReader<R>,
attributes: Vec<OwnedAttribute>,
}
impl<R: Read> ParseContext<R> {
fn next(&mut self) -> Result<XmlEvent> {
Ok(self.event_reader.next()?)
}
fn attr<T>(&self, name: &str) -> Result<T>
where
T: FromStr,
<T as FromStr>::Err: Into<Error>,
{
Ok(self
.attributes
.iter()
.filter(|attr| attr.name.local_name == name)
.map(|attr| attr.value.clone())
.next()
.ok_or_else(|| Error::MissingRequiredAttribute(name.into()))?
.parse::<T>()
.map_err(|err| err.into())?)
}
fn parse(&mut self) -> Result<Protocol> {
Ok(loop {
match self.next()? {
StartElement {
name, attributes, ..
} if name.local_name == "protocol" => {
self.attributes = attributes;
break self.protocol()?;
},
EndDocument => return Err(Error::NoProtocol),
_ => {},
}
})
}
fn protocol(&mut self) -> Result<Protocol> {
let mut protocol = Protocol::default();
protocol.name = self.attr("name")?;
Ok(loop {
match self.next()? {
StartElement {
name, attributes, ..
} => {
self.attributes = attributes;
match &*name.local_name {
"copyright" => protocol.copyright = self.copyright()?,
"description" => protocol.description = self.description()?.into(),
"interface" => protocol.interfaces.push(self.interface()?),
other => return Err(Error::UnexpectedXmlElement(other.into())),
}
},
EndElement { name } if name.local_name == "protocol" => break protocol,
other => return Err(Error::UnexpectedXmlEvent(other)),
}
})
}
fn copyright(&mut self) -> Result<String> {
let mut body = None;
Ok(loop {
match self.next()? {
Characters(data) => body = Some(data),
EndElement { name } if name.local_name == "copyright" => {
break body.unwrap_or_default();
},
other => return Err(Error::UnexpectedXmlEvent(other)),
}
})
}
fn interface(&mut self) -> Result<Interface> {
let mut interface = Interface::default();
interface.name = self.attr("name")?;
interface.version = self.attr("version")?;
Ok(loop {
match self.next()? {
StartElement {
name, attributes, ..
} => {
self.attributes = attributes;
match &*name.local_name {
"description" => interface.description = self.description()?.into(),
"request" => interface.requests.push(self.request()?),
"event" => interface.events.push(self.event()?),
"enum" => interface.enums.push(self.enumeration()?),
other => return Err(Error::UnexpectedXmlElement(other.into())),
}
},
EndElement { name } if name.local_name == "interface" => break interface,
other => return Err(Error::UnexpectedXmlEvent(other)),
}
})
}
fn request(&mut self) -> Result<Request> {
let mut request = Request::default();
request.name = self.attr("name")?;
request.destructor = self
.attr("type")
.map(|t: String| t == "destructor")
.unwrap_or(false);
request.since = self.attr("since").unwrap_or(1);
Ok(loop {
match self.next()? {
StartElement {
name, attributes, ..
} => {
self.attributes = attributes;
match &*name.local_name {
"description" => request.description = self.description()?.into(),
"arg" => request.args.push(self.arg()?),
other => return Err(Error::UnexpectedXmlElement(other.into())),
}
},
EndElement { name } if name.local_name == "request" => break request,
other => return Err(Error::UnexpectedXmlEvent(other)),
}
})
}
fn event(&mut self) -> Result<Event> {
let mut event = Event::default();
event.name = self.attr("name")?;
event.since = self.attr("since").unwrap_or(1);
Ok(loop {
match self.next()? {
StartElement {
name, attributes, ..
} => {
self.attributes = attributes;
match &*name.local_name {
"description" => event.description = self.description()?.into(),
"arg" => event.args.push(self.arg()?),
other => return Err(Error::UnexpectedXmlElement(other.into())),
}
},
EndElement { name } if name.local_name == "event" => break event,
other => return Err(Error::UnexpectedXmlEvent(other)),
}
})
}
fn arg(&mut self) -> Result<Arg> {
let mut arg = Arg::default();
arg.name = self.attr("name")?;
arg.kind = self.attr("type")?;
arg.summary = self.attr("summary").ok();
arg.interface = self.attr("interface").ok();
arg.allow_null = self.attr("allow-null").unwrap_or(false);
arg.enumeration = self.attr("enum").ok();
Ok(loop {
match self.next()? {
StartElement {
name, attributes, ..
} if name.local_name == "description" => {
self.attributes = attributes;
arg.description = self.description()?.into();
},
EndElement { name } if name.local_name == "arg" => break arg,
other => return Err(Error::UnexpectedXmlEvent(other)),
}
})
}
fn enumeration(&mut self) -> Result<Enum> {
let mut enumeration = Enum::default();
enumeration.name = self.attr("name")?;
enumeration.since = self.attr("since").unwrap_or(1);
enumeration.bitfield = self.attr("bitfield").unwrap_or(false);
Ok(loop {
match self.next()? {
StartElement {
name, attributes, ..
} => {
self.attributes = attributes;
match &*name.local_name {
"description" => enumeration.description = self.description()?.into(),
"entry" => enumeration.entries.push(self.entry()?),
other => return Err(Error::UnexpectedXmlElement(other.into())),
}
},
EndElement { name } if name.local_name == "enum" => break enumeration,
other => return Err(Error::UnexpectedXmlEvent(other)),
}
})
}
fn entry(&mut self) -> Result<Entry> {
let mut entry = Entry::default();
entry.name = self.attr("name")?;
entry.value = {
let value: String = self.attr("value")?;
let (str, radix) = if value.starts_with("0x") {
(&value[2..], 16)
} else {
(&value[..], 10)
};
u32::from_str_radix(str, radix)?
};
entry.summary = self.attr("summary").ok();
entry.since = self.attr("since").unwrap_or(1);
Ok(loop {
match self.next()? {
StartElement {
name, attributes, ..
} if name.local_name == "description" => {
self.attributes = attributes;
entry.description = self.description()?.into();
},
EndElement { name } if name.local_name == "entry" => break entry,
other => return Err(Error::UnexpectedXmlEvent(other)),
}
})
}
fn description(&mut self) -> Result<Description> {
let mut description = Description::default();
description.summary = self.attr("summary")?;
Ok(loop {
match self.next()? {
Characters(data) => description.body = data,
EndElement { name } if name.local_name == "description" => {
break description;
},
other => return Err(Error::UnexpectedXmlEvent(other)),
}
})
}
}
pub fn parse<R: Read>(reader: R) -> std::result::Result<Protocol, Error> {
let event_reader = EventReader::new_with_config(
reader,
ParserConfig::new()
.trim_whitespace(true)
.cdata_to_characters(true),
);
ParseContext {
event_reader,
attributes: Vec::new(),
}
.parse()
}