xml-to-wire: add new utility
This commit is contained in:
parent
aa70bde75d
commit
6d8fb37db4
7 changed files with 829 additions and 11 deletions
28
Cargo.lock
generated
28
Cargo.lock
generated
|
|
@ -106,9 +106,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.95"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
|
|
@ -1007,18 +1007,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.38.0"
|
||||
version = "0.38.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8927b0664f5c5a98265138b7e3f90aa19a6b21353182469ace36d4ac527b7b1b"
|
||||
checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
|
@ -1352,18 +1352,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.11"
|
||||
version = "2.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.11"
|
||||
version = "2.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -1733,6 +1733,14 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xml-to-wire"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quick-xml",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ name = "jay"
|
|||
path = "src/main.rs"
|
||||
|
||||
[workspace]
|
||||
members = ["jay-config", "toml-config", "algorithms", "toml-spec", "wire-to-xml"]
|
||||
members = ["jay-config", "toml-config", "algorithms", "toml-spec", "wire-to-xml", "xml-to-wire"]
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
|
|
|
|||
8
xml-to-wire/Cargo.toml
Normal file
8
xml-to-wire/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "xml-to-wire"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
quick-xml = "0.38.3"
|
||||
thiserror = "2.0.16"
|
||||
80
xml-to-wire/src/ast.rs
Normal file
80
xml-to-wire/src/ast.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
pub(crate) struct Protocol {
|
||||
pub(crate) _name: String,
|
||||
pub(crate) _copyright: Option<Copyright>,
|
||||
pub(crate) _description: Option<Description>,
|
||||
pub(crate) interfaces: Vec<Interface>,
|
||||
}
|
||||
|
||||
pub(crate) struct Copyright {
|
||||
pub(crate) _body: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Description {
|
||||
pub(crate) _summary: Option<String>,
|
||||
pub(crate) _body: String,
|
||||
}
|
||||
|
||||
pub(crate) struct Interface {
|
||||
pub(crate) name: String,
|
||||
pub(crate) _version: u32,
|
||||
pub(crate) _description: Option<Description>,
|
||||
pub(crate) messages: Vec<Message>,
|
||||
pub(crate) _enums: Vec<Enum>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Arg {
|
||||
pub(crate) name: String,
|
||||
pub(crate) ty: ArgType,
|
||||
pub(crate) _summary: Option<String>,
|
||||
pub(crate) _description: Option<Description>,
|
||||
pub(crate) interface: Option<String>,
|
||||
pub(crate) allow_null: bool,
|
||||
pub(crate) enum_: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub(crate) enum ArgType {
|
||||
NewId,
|
||||
Int,
|
||||
Uint,
|
||||
Fixed,
|
||||
String,
|
||||
Object,
|
||||
Array,
|
||||
Fd,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub(crate) enum MessageType {
|
||||
Destructor,
|
||||
}
|
||||
|
||||
pub(crate) struct Entry {
|
||||
pub(crate) _name: String,
|
||||
pub(crate) _value: String,
|
||||
pub(crate) _value_u32: u32,
|
||||
pub(crate) _summary: Option<String>,
|
||||
pub(crate) _since: Option<u32>,
|
||||
pub(crate) _deprecated_since: Option<u32>,
|
||||
pub(crate) _description: Option<Description>,
|
||||
}
|
||||
|
||||
pub(crate) struct Enum {
|
||||
pub(crate) _name: String,
|
||||
pub(crate) _since: Option<u32>,
|
||||
pub(crate) _bitfield: bool,
|
||||
pub(crate) _description: Option<Description>,
|
||||
pub(crate) _entries: Vec<Entry>,
|
||||
}
|
||||
|
||||
pub(crate) struct Message {
|
||||
pub(crate) name: String,
|
||||
pub(crate) request: bool,
|
||||
pub(crate) ty: Option<MessageType>,
|
||||
pub(crate) since: Option<u32>,
|
||||
pub(crate) _deprecated_since: Option<u32>,
|
||||
pub(crate) _description: Option<Description>,
|
||||
pub(crate) args: Vec<Arg>,
|
||||
}
|
||||
130
xml-to-wire/src/builder.rs
Normal file
130
xml-to-wire/src/builder.rs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
use {
|
||||
crate::{
|
||||
ast::{ArgType, Message, MessageType},
|
||||
parser::{ParserError, parse},
|
||||
},
|
||||
std::{
|
||||
fs::File,
|
||||
io::{self, BufWriter, Write},
|
||||
mem,
|
||||
},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum BuilderError {
|
||||
#[error("Could not process {0}")]
|
||||
File(String, #[source] Box<BuilderError>),
|
||||
#[error("Could not read file")]
|
||||
ReadFile(#[source] io::Error),
|
||||
#[error("Could not parse file")]
|
||||
ParseFile(#[source] ParserError),
|
||||
#[error("Could not format file")]
|
||||
FormatFile(#[source] io::Error),
|
||||
#[error("Could not open {0} for writing")]
|
||||
OpenFile(String, #[source] io::Error),
|
||||
}
|
||||
|
||||
pub fn handle_file(path: &str) -> Result<(), BuilderError> {
|
||||
handle_file_(path).map_err(|e| BuilderError::File(path.to_string(), Box::new(e)))
|
||||
}
|
||||
|
||||
fn handle_file_(path: &str) -> Result<(), BuilderError> {
|
||||
let data = std::fs::read(path).map_err(BuilderError::ReadFile)?;
|
||||
let protocols = parse(&data).map_err(BuilderError::ParseFile)?;
|
||||
for protocol in protocols {
|
||||
for i in protocol.interfaces {
|
||||
let path = format!("wire/{}.txt", i.name);
|
||||
let mut file =
|
||||
BufWriter::new(File::create(&path).map_err(|e| BuilderError::OpenFile(path, e))?);
|
||||
let mut not_first = false;
|
||||
let mut handle_msg = |msg: &Message| -> Result<(), io::Error> {
|
||||
if not_first {
|
||||
writeln!(file)?;
|
||||
}
|
||||
not_first = true;
|
||||
let ty = match msg.request {
|
||||
true => "request",
|
||||
false => "event",
|
||||
};
|
||||
write!(file, "{ty} {} ", msg.name)?;
|
||||
if msg.ty.is_some() || msg.since.is_some() {
|
||||
write!(file, "(")?;
|
||||
let mut needs_comma = false;
|
||||
let handle_comma =
|
||||
|needs_comma: &mut bool, file: &mut BufWriter<File>| -> io::Result<()> {
|
||||
if mem::take(needs_comma) {
|
||||
write!(file, ", ")?;
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
if let Some(ty) = msg.ty {
|
||||
match ty {
|
||||
MessageType::Destructor => {
|
||||
handle_comma(&mut needs_comma, &mut file)?;
|
||||
write!(file, "destructor")?;
|
||||
needs_comma = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(s) = msg.since {
|
||||
handle_comma(&mut needs_comma, &mut file)?;
|
||||
write!(file, "since = {}", s)?;
|
||||
needs_comma = true;
|
||||
}
|
||||
let _ = needs_comma;
|
||||
write!(file, ") ")?;
|
||||
}
|
||||
writeln!(file, "{{")?;
|
||||
let mut args = msg.args.iter().peekable();
|
||||
while let Some(arg) = args.next() {
|
||||
if arg.ty == ArgType::Uint
|
||||
&& arg.enum_.is_none()
|
||||
&& let Some(prefix) = arg.name.strip_suffix("_hi")
|
||||
&& let Some(next) = args.peek()
|
||||
&& next.ty == ArgType::Uint
|
||||
&& next.enum_.is_none()
|
||||
&& next.name.strip_prefix(prefix) == Some("_lo")
|
||||
{
|
||||
writeln!(file, " {prefix}: u64,")?;
|
||||
args.next();
|
||||
continue;
|
||||
}
|
||||
write!(file, " {}: ", arg.name)?;
|
||||
'ty: {
|
||||
let ty = match arg.ty {
|
||||
ArgType::NewId | ArgType::Object => {
|
||||
write!(
|
||||
file,
|
||||
"id({})",
|
||||
arg.interface.as_deref().unwrap_or("object")
|
||||
)?;
|
||||
if arg.ty == ArgType::NewId {
|
||||
write!(file, " (new)")?;
|
||||
}
|
||||
break 'ty;
|
||||
}
|
||||
ArgType::Int => "i32",
|
||||
ArgType::Uint => "u32",
|
||||
ArgType::Fixed => "fixed",
|
||||
ArgType::String => match arg.allow_null {
|
||||
true => "optstr",
|
||||
false => "str",
|
||||
},
|
||||
ArgType::Array => "array(pod(u8))",
|
||||
ArgType::Fd => "fd",
|
||||
};
|
||||
write!(file, "{}", ty)?;
|
||||
}
|
||||
writeln!(file, ",")?;
|
||||
}
|
||||
writeln!(file, "}}")?;
|
||||
Ok(())
|
||||
};
|
||||
for m in &i.messages {
|
||||
handle_msg(m).map_err(BuilderError::FormatFile)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
15
xml-to-wire/src/main.rs
Normal file
15
xml-to-wire/src/main.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
use {
|
||||
crate::builder::{BuilderError, handle_file},
|
||||
std::env::args,
|
||||
};
|
||||
|
||||
mod ast;
|
||||
mod builder;
|
||||
mod parser;
|
||||
|
||||
fn main() -> Result<(), BuilderError> {
|
||||
for xml in args().skip(1) {
|
||||
handle_file(&xml)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
577
xml-to-wire/src/parser.rs
Normal file
577
xml-to-wire/src/parser.rs
Normal file
|
|
@ -0,0 +1,577 @@
|
|||
use {
|
||||
crate::ast::{
|
||||
Arg, ArgType, Copyright, Description, Entry, Enum, Interface, Message, MessageType,
|
||||
Protocol,
|
||||
},
|
||||
quick_xml::{
|
||||
Reader,
|
||||
events::{
|
||||
Event,
|
||||
attributes::{AttrError, Attribute, Attributes},
|
||||
},
|
||||
},
|
||||
std::{
|
||||
borrow::Cow,
|
||||
num::ParseIntError,
|
||||
str::{FromStr, ParseBoolError},
|
||||
string::FromUtf8Error,
|
||||
},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub(crate) enum ParserError {
|
||||
#[error("Could not parse a protocol element")]
|
||||
Protocol(#[from] ProtocolError),
|
||||
#[error("Could not read the next event")]
|
||||
ReadEvent(#[from] quick_xml::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub(crate) enum ProtocolError {
|
||||
#[error("Could not parse an attribute")]
|
||||
Attribute(#[from] AttributeError),
|
||||
#[error("Protocol does not have a name")]
|
||||
MissingName,
|
||||
#[error("Could not read the next event")]
|
||||
ReadEvent(#[from] quick_xml::Error),
|
||||
#[error("Could not the copyright element")]
|
||||
Copyright(#[from] CopyrightError),
|
||||
#[error("Could not parse the description element")]
|
||||
Description(#[from] DescriptionError),
|
||||
#[error("Could not parse an interface element")]
|
||||
Interface(#[from] InterfaceError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub(crate) enum CopyrightError {
|
||||
#[error("Could not read the next event")]
|
||||
ReadEvent(#[from] quick_xml::Error),
|
||||
#[error("Could not decode the body as UTF-8")]
|
||||
DecodeUtf8(#[source] FromUtf8Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub(crate) enum DescriptionError {
|
||||
#[error("Could not parse an attribute")]
|
||||
Attribute(#[from] AttributeError),
|
||||
#[error("Could not read the next event")]
|
||||
ReadEvent(#[from] quick_xml::Error),
|
||||
#[error("Could not decode the body as UTF-8")]
|
||||
DecodeUtf8(#[source] FromUtf8Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub(crate) enum InterfaceError {
|
||||
#[error("Interface has no name")]
|
||||
MissingName,
|
||||
#[error("Interface has no version")]
|
||||
MissingVersion,
|
||||
#[error("Could not parse an attribute")]
|
||||
Attribute(#[from] AttributeError),
|
||||
#[error("Could not read the next event")]
|
||||
ReadEvent(#[from] quick_xml::Error),
|
||||
#[error("Could not parse the version")]
|
||||
Version(#[source] ParseIntError),
|
||||
#[error("Could not parse a request element")]
|
||||
Request(#[source] MessageError),
|
||||
#[error("Could not parse an event element")]
|
||||
Event(#[source] MessageError),
|
||||
#[error("Could not parse the description element")]
|
||||
Description(#[from] DescriptionError),
|
||||
#[error("Could not parse an enum element")]
|
||||
Enum(#[from] EnumError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub(crate) enum MessageError {
|
||||
#[error("Message has no name")]
|
||||
MissingName,
|
||||
#[error("Could not parse an attribute")]
|
||||
Attribute(#[from] AttributeError),
|
||||
#[error("Could not read the next event")]
|
||||
ReadEvent(#[from] quick_xml::Error),
|
||||
#[error("Could not parse the since attribute")]
|
||||
Since(#[source] ParseIntError),
|
||||
#[error("Could not parse the deprecated-since attribute")]
|
||||
DeprecatedSince(#[source] ParseIntError),
|
||||
#[error("Unknown message type {}", .0)]
|
||||
UnknownMessageType(String),
|
||||
#[error("Could not parse an argument element")]
|
||||
Arg(#[from] ArgError),
|
||||
#[error("Could not the description element")]
|
||||
Description(#[from] DescriptionError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub(crate) enum ArgError {
|
||||
#[error("Argument has no name")]
|
||||
MissingName,
|
||||
#[error("Argument has no type")]
|
||||
MissingType,
|
||||
#[error("Could not parse an attribute")]
|
||||
Attribute(#[from] AttributeError),
|
||||
#[error("Could not read the next event")]
|
||||
ReadEvent(#[from] quick_xml::Error),
|
||||
#[error("Could not parse the allow-null attribute")]
|
||||
AllowNull(#[source] ParseBoolError),
|
||||
#[error("Unknown arg type {}", .0)]
|
||||
UnknownArgType(String),
|
||||
#[error("Could not the description element")]
|
||||
Description(#[from] DescriptionError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub(crate) enum EnumError {
|
||||
#[error("Enum has no name")]
|
||||
MissingName,
|
||||
#[error("Could not parse an attribute")]
|
||||
Attribute(#[from] AttributeError),
|
||||
#[error("Could not read the next event")]
|
||||
ReadEvent(#[from] quick_xml::Error),
|
||||
#[error("Could not parse the allow-null attribute")]
|
||||
AllowNull(#[source] ParseBoolError),
|
||||
#[error("Could not the description element")]
|
||||
Description(#[from] DescriptionError),
|
||||
#[error("Could not parse the since attribute")]
|
||||
Since(#[source] ParseIntError),
|
||||
#[error("Could not an entry element")]
|
||||
Entry(#[from] EntryError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub(crate) enum EntryError {
|
||||
#[error("Entry has no name")]
|
||||
MissingName,
|
||||
#[error("Entry has no value")]
|
||||
MissingValue,
|
||||
#[error("Value could not be parsed")]
|
||||
InvalidValue(#[source] ParseIntError),
|
||||
#[error("Could not parse an attribute")]
|
||||
Attribute(#[from] AttributeError),
|
||||
#[error("Could not read the next event")]
|
||||
ReadEvent(#[from] quick_xml::Error),
|
||||
#[error("Could not the description element")]
|
||||
Description(#[from] DescriptionError),
|
||||
#[error("Could not parse the since attribute")]
|
||||
Since(#[source] ParseIntError),
|
||||
#[error("Could not parse the deprecated-since attribute")]
|
||||
DeprecatedSince(#[source] ParseIntError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub(crate) enum AttributeError {
|
||||
#[error("quick_xml returned an error")]
|
||||
QuickXml(#[from] AttrError),
|
||||
#[error("Could not decode the value as UTF-8")]
|
||||
DecodeUtf8(#[from] quick_xml::Error),
|
||||
}
|
||||
|
||||
pub(crate) fn parse(input: &[u8]) -> Result<Vec<Protocol>, ParserError> {
|
||||
let mut reader = Reader::from_reader(input);
|
||||
let mut protocols = Vec::new();
|
||||
loop {
|
||||
let event = reader.read_event().map_err(ParserError::ReadEvent)?;
|
||||
let (start, empty) = match event {
|
||||
Event::Start(s) => (s, false),
|
||||
Event::Empty(s) => (s, true),
|
||||
Event::Eof => break,
|
||||
_ => continue,
|
||||
};
|
||||
match start.local_name().as_ref() {
|
||||
b"protocol" => protocols.push(parse_protocol(&mut reader, start.attributes(), empty)?),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
Ok(protocols)
|
||||
}
|
||||
|
||||
macro_rules! parse_attr {
|
||||
($attr:expr) => {
|
||||
match $attr {
|
||||
Ok(ref attr) => parse_attr(attr),
|
||||
Err(e) => return Err(AttributeError::QuickXml(e).into()),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn parse_attr<'a>(attr: &'a Attribute) -> Result<(&'a [u8], Cow<'a, str>), AttributeError> {
|
||||
let name = attr.key.local_name().into_inner();
|
||||
let value = attr.unescape_value().map_err(AttributeError::DecodeUtf8)?;
|
||||
Ok((name, value))
|
||||
}
|
||||
|
||||
fn parse_protocol(
|
||||
reader: &mut Reader<&[u8]>,
|
||||
attributes: Attributes,
|
||||
empty: bool,
|
||||
) -> Result<Protocol, ProtocolError> {
|
||||
let mut name = None;
|
||||
for attr in attributes {
|
||||
let (n, value) = parse_attr!(attr)?;
|
||||
match n {
|
||||
b"name" => name = Some(value.into_owned()),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
let mut copyright = None;
|
||||
let mut description = None;
|
||||
let mut interfaces = vec![];
|
||||
if !empty {
|
||||
loop {
|
||||
let event = reader.read_event().map_err(ProtocolError::ReadEvent)?;
|
||||
let (start, empty) = match event {
|
||||
Event::Start(s) => (s, false),
|
||||
Event::End(_) => break,
|
||||
Event::Empty(s) => (s, true),
|
||||
_ => continue,
|
||||
};
|
||||
match start.local_name().as_ref() {
|
||||
b"copyright" => {
|
||||
copyright = Some(parse_copyright(reader, start.attributes(), empty)?)
|
||||
}
|
||||
b"description" => {
|
||||
description = Some(parse_description(reader, start.attributes(), empty)?)
|
||||
}
|
||||
b"interface" => {
|
||||
interfaces.push(parse_interface(reader, start.attributes(), empty)?)
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Protocol {
|
||||
_name: name.ok_or(ProtocolError::MissingName)?,
|
||||
_copyright: copyright,
|
||||
_description: description,
|
||||
interfaces,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_copyright(
|
||||
reader: &mut Reader<&[u8]>,
|
||||
_attributes: Attributes,
|
||||
empty: bool,
|
||||
) -> Result<Copyright, CopyrightError> {
|
||||
let mut body = Vec::new();
|
||||
if !empty {
|
||||
loop {
|
||||
let event = reader.read_event().map_err(CopyrightError::ReadEvent)?;
|
||||
match event {
|
||||
Event::Text(s) => body.extend_from_slice(s.as_ref()),
|
||||
Event::End(_) => break,
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Copyright {
|
||||
_body: String::from_utf8(body).map_err(CopyrightError::DecodeUtf8)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_description(
|
||||
reader: &mut Reader<&[u8]>,
|
||||
attributes: Attributes,
|
||||
empty: bool,
|
||||
) -> Result<Description, DescriptionError> {
|
||||
let mut summary = None;
|
||||
for attr in attributes {
|
||||
let (n, value) = parse_attr!(attr)?;
|
||||
match n {
|
||||
b"summary" => summary = Some(value.into_owned()),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
let mut body = Vec::new();
|
||||
if !empty {
|
||||
loop {
|
||||
let event = reader.read_event().map_err(DescriptionError::ReadEvent)?;
|
||||
match event {
|
||||
Event::Text(s) => body.extend_from_slice(s.as_ref()),
|
||||
Event::End(_) => break,
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Description {
|
||||
_summary: summary,
|
||||
_body: String::from_utf8(body).map_err(DescriptionError::DecodeUtf8)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_interface(
|
||||
reader: &mut Reader<&[u8]>,
|
||||
attributes: Attributes,
|
||||
empty: bool,
|
||||
) -> Result<Interface, InterfaceError> {
|
||||
let mut name = None;
|
||||
let mut version = None;
|
||||
for attr in attributes {
|
||||
let (n, value) = parse_attr!(attr)?;
|
||||
match n {
|
||||
b"name" => name = Some(value.into_owned()),
|
||||
b"version" => version = Some(value.parse().map_err(InterfaceError::Version)?),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
let mut description = None;
|
||||
let mut messages = Vec::new();
|
||||
let mut enums = Vec::new();
|
||||
if !empty {
|
||||
loop {
|
||||
let event = reader.read_event().map_err(InterfaceError::ReadEvent)?;
|
||||
let (start, empty) = match event {
|
||||
Event::Start(s) => (s, false),
|
||||
Event::End(_) => break,
|
||||
Event::Empty(s) => (s, true),
|
||||
_ => continue,
|
||||
};
|
||||
match start.local_name().as_ref() {
|
||||
b"description" => {
|
||||
description = Some(parse_description(reader, start.attributes(), empty)?)
|
||||
}
|
||||
b"request" => messages.push(
|
||||
parse_message(reader, start.attributes(), empty, true)
|
||||
.map_err(InterfaceError::Request)?,
|
||||
),
|
||||
b"event" => messages.push(
|
||||
parse_message(reader, start.attributes(), empty, false)
|
||||
.map_err(InterfaceError::Event)?,
|
||||
),
|
||||
b"enum" => enums.push(parse_enum(reader, start.attributes(), empty)?),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Interface {
|
||||
name: name.ok_or(InterfaceError::MissingName)?,
|
||||
_version: version.ok_or(InterfaceError::MissingVersion)?,
|
||||
_description: description,
|
||||
messages,
|
||||
_enums: enums,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_message(
|
||||
reader: &mut Reader<&[u8]>,
|
||||
attributes: Attributes,
|
||||
empty: bool,
|
||||
request: bool,
|
||||
) -> Result<Message, MessageError> {
|
||||
let mut name = None;
|
||||
let mut ty = None;
|
||||
let mut since = None;
|
||||
let mut deprecated_since = None;
|
||||
for attr in attributes {
|
||||
let (n, value) = parse_attr!(attr)?;
|
||||
match n {
|
||||
b"name" => name = Some(value.into_owned()),
|
||||
b"type" => match value.as_ref() {
|
||||
"destructor" => ty = Some(MessageType::Destructor),
|
||||
_ => return Err(MessageError::UnknownMessageType(value.into_owned())),
|
||||
},
|
||||
b"since" => since = Some(value.parse().map_err(MessageError::Since)?),
|
||||
b"deprecated-since" => {
|
||||
deprecated_since = Some(value.parse().map_err(MessageError::DeprecatedSince)?)
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
let mut description = None;
|
||||
let mut args = Vec::new();
|
||||
if !empty {
|
||||
loop {
|
||||
let event = reader.read_event().map_err(MessageError::ReadEvent)?;
|
||||
let (start, empty) = match event {
|
||||
Event::Start(s) => (s, false),
|
||||
Event::End(_) => break,
|
||||
Event::Empty(s) => (s, true),
|
||||
_ => continue,
|
||||
};
|
||||
match start.local_name().as_ref() {
|
||||
b"description" => {
|
||||
description = Some(parse_description(reader, start.attributes(), empty)?)
|
||||
}
|
||||
b"arg" => args.push(parse_arg(reader, start.attributes(), empty)?),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Message {
|
||||
name: name.ok_or(MessageError::MissingName)?,
|
||||
request,
|
||||
ty,
|
||||
since,
|
||||
_deprecated_since: deprecated_since,
|
||||
_description: description,
|
||||
args,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_arg(
|
||||
reader: &mut Reader<&[u8]>,
|
||||
attributes: Attributes,
|
||||
empty: bool,
|
||||
) -> Result<Arg, ArgError> {
|
||||
let mut name = None;
|
||||
let mut ty = None;
|
||||
let mut summary = None;
|
||||
let mut interface = None;
|
||||
let mut allow_null = None;
|
||||
let mut enum_ = None;
|
||||
for attr in attributes {
|
||||
let (n, value) = parse_attr!(attr)?;
|
||||
match n {
|
||||
b"name" => name = Some(value.into_owned()),
|
||||
b"type" => {
|
||||
ty = Some(match value.as_ref() {
|
||||
"int" => ArgType::Int,
|
||||
"uint" => ArgType::Uint,
|
||||
"fixed" => ArgType::Fixed,
|
||||
"string" => ArgType::String,
|
||||
"array" => ArgType::Array,
|
||||
"fd" => ArgType::Fd,
|
||||
"new_id" => ArgType::NewId,
|
||||
"object" => ArgType::Object,
|
||||
_ => return Err(ArgError::UnknownArgType(value.into_owned())),
|
||||
})
|
||||
}
|
||||
b"summary" => summary = Some(value.into_owned()),
|
||||
b"interface" => interface = Some(value.into_owned()),
|
||||
b"allow-null" => allow_null = Some(value.parse().map_err(ArgError::AllowNull)?),
|
||||
b"enum" => enum_ = Some(value.into_owned()),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
let mut description = None;
|
||||
if !empty {
|
||||
loop {
|
||||
let event = reader.read_event().map_err(ArgError::ReadEvent)?;
|
||||
let (start, empty) = match event {
|
||||
Event::Start(s) => (s, false),
|
||||
Event::End(_) => break,
|
||||
Event::Empty(s) => (s, true),
|
||||
_ => continue,
|
||||
};
|
||||
match start.local_name().as_ref() {
|
||||
b"description" => {
|
||||
description = Some(parse_description(reader, start.attributes(), empty)?)
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Arg {
|
||||
name: name.ok_or(ArgError::MissingName)?,
|
||||
ty: ty.ok_or(ArgError::MissingType)?,
|
||||
_summary: summary,
|
||||
_description: description,
|
||||
interface,
|
||||
allow_null: allow_null.unwrap_or_default(),
|
||||
enum_,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_enum(
|
||||
reader: &mut Reader<&[u8]>,
|
||||
attributes: Attributes,
|
||||
empty: bool,
|
||||
) -> Result<Enum, EnumError> {
|
||||
let mut name = None;
|
||||
let mut since = None;
|
||||
let mut bitfield = None;
|
||||
for attr in attributes {
|
||||
let (n, v) = parse_attr!(attr)?;
|
||||
match n {
|
||||
b"name" => name = Some(v.into_owned()),
|
||||
b"since" => since = Some(v.parse().map_err(EnumError::Since)?),
|
||||
b"bitfield" => bitfield = Some(v.parse().map_err(EnumError::AllowNull)?),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
let mut description = None;
|
||||
let mut entries = Vec::new();
|
||||
if !empty {
|
||||
loop {
|
||||
let event = reader.read_event().map_err(EnumError::ReadEvent)?;
|
||||
let (start, empty) = match event {
|
||||
Event::Start(s) => (s, false),
|
||||
Event::End(_) => break,
|
||||
Event::Empty(s) => (s, true),
|
||||
_ => continue,
|
||||
};
|
||||
match start.local_name().as_ref() {
|
||||
b"description" => {
|
||||
description = Some(parse_description(reader, start.attributes(), empty)?)
|
||||
}
|
||||
b"entry" => entries.push(parse_entry(reader, start.attributes(), empty)?),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Enum {
|
||||
_name: name.ok_or(EnumError::MissingName)?,
|
||||
_since: since,
|
||||
_bitfield: bitfield.unwrap_or_default(),
|
||||
_description: description,
|
||||
_entries: entries,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_entry(
|
||||
reader: &mut Reader<&[u8]>,
|
||||
attributes: Attributes,
|
||||
empty: bool,
|
||||
) -> Result<Entry, EntryError> {
|
||||
let mut name = None;
|
||||
let mut value = None;
|
||||
let mut summary = None;
|
||||
let mut since = None;
|
||||
let mut deprecated_since = None;
|
||||
for attr in attributes {
|
||||
let (n, v) = parse_attr!(attr)?;
|
||||
match n {
|
||||
b"name" => name = Some(v.into_owned()),
|
||||
b"value" => value = Some(v.into_owned()),
|
||||
b"summary" => summary = Some(v.into_owned()),
|
||||
b"since" => since = Some(v.parse().map_err(EntryError::Since)?),
|
||||
b"deprecated-since" => {
|
||||
deprecated_since = Some(v.parse().map_err(EntryError::DeprecatedSince)?)
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
let mut description = None;
|
||||
if !empty {
|
||||
loop {
|
||||
let event = reader.read_event().map_err(EntryError::ReadEvent)?;
|
||||
let (start, empty) = match event {
|
||||
Event::Start(s) => (s, false),
|
||||
Event::End(_) => break,
|
||||
Event::Empty(s) => (s, true),
|
||||
_ => continue,
|
||||
};
|
||||
match start.local_name().as_ref() {
|
||||
b"description" => {
|
||||
description = Some(parse_description(reader, start.attributes(), empty)?)
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
let value = value.ok_or(EntryError::MissingValue)?;
|
||||
let value_u32 = if let Some(value) = value.strip_prefix("0x") {
|
||||
u32::from_str_radix(value, 16).map_err(EntryError::InvalidValue)?
|
||||
} else {
|
||||
u32::from_str(&value).map_err(EntryError::InvalidValue)?
|
||||
};
|
||||
Ok(Entry {
|
||||
_name: name.ok_or(EntryError::MissingName)?,
|
||||
_value: value,
|
||||
_value_u32: value_u32,
|
||||
_summary: summary,
|
||||
_since: since,
|
||||
_deprecated_since: deprecated_since,
|
||||
_description: description,
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue