From 96171a45f5a68e2bd720654669392b7af435e841 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 1 Nov 2021 22:39:44 +0100 Subject: [PATCH] Switch to quick-xml --- Cargo.lock | 17 +- Cargo.toml | 2 +- src/parser.rs | 1618 +++++++++++++++++++++++++--------------------- src/xmlparser.rs | 557 ++++------------ 4 files changed, 1046 insertions(+), 1148 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe26b0ab7..c70eb94be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,10 +73,10 @@ dependencies = [ "hprof", "log 0.4.14", "once_cell", + "quick-xml", "regex", "rustdoc-stripper", "toml", - "xml-rs", ] [[package]] @@ -135,6 +135,15 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +[[package]] +name = "quick-xml" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8533f14c8382aaad0d592c812ac3b826162128b65662331e1127b45c3d18536b" +dependencies = [ + "memchr", +] + [[package]] name = "regex" version = "1.5.4" @@ -178,9 +187,3 @@ name = "unicode-width" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" - -[[package]] -name = "xml-rs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" diff --git a/Cargo.toml b/Cargo.toml index a461c0399..fa045123f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ edition = "2018" bitflags = "1.0" getopts = "0.2.21" getter_rules = { package = "fix-getters-rules", version = "0.3.0", default-features = false } -xml-rs = "0.8" +quick-xml = "0.22" toml = { version = "0.5" , features = ["preserve_order"] } env_logger = { version = "0.9", default-features = false } once_cell = "1.0" diff --git a/src/parser.rs b/src/parser.rs index 0d8f7e019..278138fac 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,9 +1,6 @@ -use crate::{ - library::*, - version::Version, - xmlparser::{Element, XmlParser}, -}; +use crate::{library::*, version::Version, xmlparser::XmlParser}; use log::{trace, warn}; +use quick_xml::events::BytesStart; use std::{ path::{Path, PathBuf}, str::FromStr, @@ -11,6 +8,99 @@ use std::{ const EMPTY_CTYPE: &str = "/*EMPTY*/"; +macro_rules! from_utf8 { + ($s:expr) => { + std::str::from_utf8(&$s).expect("failed to convert to str") + }; +} + +macro_rules! attr_value { + ($s:expr) => { + std::str::from_utf8(&$s.value).unwrap() + }; +} + +macro_rules! attr { + ($elem:ident, $attr_name:expr) => {{ + $elem + .attributes() + .filter_map(|n| n.ok()) + .find(|n| n.key == $attr_name) + }}; +} + +macro_rules! attr_bool { + ($elem:ident, $attr_name:expr, $default:expr) => {{ + if let Some(attr) = $elem + .attributes() + .filter_map(|n| n.ok()) + .find(|n| n.key == $attr_name) + { + &*attr.value == b"1" + } else { + $default + } + }}; +} + +macro_rules! attr_or_err { + ($elem:ident, $attr_name:expr) => {{ + let name = attr!($elem, $attr_name); + name.ok_or_else(|| { + format!( + "Attribute `{}` on element <{}> is required", + unsafe { std::str::from_utf8_unchecked($attr_name) }, + unsafe { std::str::from_utf8_unchecked($elem.name()) }, + ) + }) + }}; +} + +macro_rules! elements { + ($parser:ident, $buf:ident, $var_name:ident, $callback:block) => {{ + loop { + match $parser.next_event($buf) { + Ok(quick_xml::events::Event::Start(_)) => { + let $var_name = $parser.next_element($buf)?; + $callback?; + $parser.end_element($buf)?; + } + _ => break, + } + } + }}; +} + +// Basically the same as `elements` but fill a vec with the results. +macro_rules! elements_vec { + ($parser:ident, $buf:ident, $var_name:ident, $callback:block) => {{ + let mut results = Vec::new(); + loop { + match $parser.next_event($buf) { + Ok(quick_xml::events::Event::Start(_)) => { + let $var_name = $parser.next_element($buf)?; + results.push($callback?); + $parser.end_element($buf)?; + } + _ => break, + } + } + results + }}; +} + +macro_rules! read_object_c_type { + ($parser:ident, $elem:ident) => {{ + let attr = attr!($elem, b"type").or_else(|| attr!($elem, b"type-name")); + attr.ok_or_else(|| { + $parser.error(&format!( + "Missing `c:type`/`glib:type-name` attributes on element <{}>", + from_utf8!($elem.name()), + )) + }) + }}; +} + pub fn is_empty_c_type(c_type: &str) -> bool { c_type == EMPTY_CTYPE } @@ -21,138 +111,177 @@ impl Library { dirs: &[P], libs: &mut Vec, ) -> Result<(), String> { + let mut buf = Vec::new(); + for dir in dirs { + buf.clear(); let dir: &Path = dir.as_ref(); let file_name = make_file_name(dir, &libs[libs.len() - 1]); - let mut parser = match XmlParser::from_path(&file_name) { - Ok(p) => p, - _ => continue, - }; - return parser.document(|p, _| { - p.element_with_name("repository", |sub_parser, _elem| { - self.read_repository(dirs, sub_parser, libs) - }) - }); + + let mut p = XmlParser::new(file_name)?; + + if let Err(e) = p.get_next_if_tag_is(&mut buf, b"repository") { + return Err(e); + } + return self.read_repository(dirs, &mut p, &mut buf, libs); + // match reader.read_event(&mut buf) { + // Ok(Event::Start(ref e)) => { + // match e.name() { + // b"repository" => self.read_repository(dirs, libs, &mut reader, &mut buf), + // tag => return Err(), + // } + // } + // } + // Ok(Event::Text(e)) => txt.push(e.unescape_and_decode(&reader).unwrap()), + // Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), + // Ok(Event::Eof) => break, + // } + // return parser.document(|p, _| { + // p.element_with_name("repository", |sub_parser, _elem| { + // self.read_repository(dirs, sub_parser, libs) + // }) + // }); } Err(format!("Couldn't find `{}`...", &libs[libs.len() - 1])) } - fn read_repository>( + fn read_repository<'a, P: AsRef>( &mut self, dirs: &[P], - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, libs: &mut Vec, ) -> Result<(), String> { let mut package = None; let mut includes = Vec::new(); - parser.elements(|parser, elem| match elem.name() { - "include" => { - match (elem.attr("name"), elem.attr("version")) { - (Some(name), Some(ver)) => { - if self.find_namespace(name).is_none() { - let lib = format!("{}-{}", name, ver); - if libs.iter().any(|x| *x == lib) { - return Err(format!( - "`{}` includes itself (full path:`{}`)!", - lib, - libs.join("::") - )); + elements!(parser, buf, elem, { + match elem.name() { + b"include" => { + if let Some(name) = attr!(elem, b"name") { + let name = attr_value!(name); + if let Some(ver) = attr!(elem, b"version") { + if self.find_namespace(name).is_none() { + let lib = format!("{}-{}", name, attr_value!(ver)); + if libs.iter().any(|x| *x == lib) { + return Err(format!( + "`{}` includes itself (full path:`{}`)!", + lib, + libs.join("::") + )); + } + libs.push(lib); + self.read_file(dirs, libs)?; + libs.pop(); } - libs.push(lib); - self.read_file(dirs, libs)?; - libs.pop(); + } else { + includes.push(name.to_owned()); } } - (Some(name), None) => includes.push(name.to_owned()), - _ => {} + Ok(()) } - Ok(()) - } - "package" => { - // Take the first package element and ignore any other ones. - if package.is_none() { - let name = elem.attr_required("name")?; - package = Some(name.to_owned()); + b"package" => { + // Take the first package element and ignore any other ones. + if package.is_none() { + let name = attr_or_err!(elem, b"name")?; + package = Some(attr_value!(name).to_owned()); + } + Ok(()) } - Ok(()) - } - "namespace" => { - self.read_namespace(parser, elem, package.take(), std::mem::take(&mut includes)) + b"namespace" => self.read_namespace( + parser, + buf, + &elem, + package.take(), + std::mem::take(&mut includes), + ), + b"attribute" => parser.ignore_element(buf), + _ => return Err(parser.unexpected_element(&elem)), } - "attribute" => parser.ignore_element(), - _ => Err(parser.unexpected_element(elem)), - })?; + }); Ok(()) } - fn read_namespace( + fn read_namespace<'a>( &mut self, - parser: &mut XmlParser<'_>, - elem: &Element, + parser: &mut XmlParser, + buf: &'a mut Vec, + elem: &BytesStart<'_>, package: Option, c_includes: Vec, ) -> Result<(), String> { - let ns_name = elem.attr_required("name")?; + let ns_name = attr_or_err!(elem, b"name")?; + let ns_name = attr_value!(ns_name); let ns_id = self.add_namespace(ns_name); { let ns = self.namespace_mut(ns_id); ns.package_name = package; ns.c_includes = c_includes; - if let Some(s) = elem.attr("shared-library") { - ns.shared_library = s.split(',').map(String::from).collect(); + if let Some(s) = attr!(elem, b"shared-library") { + ns.shared_library = attr_value!(s).split(',').map(String::from).collect(); } - if let Some(s) = elem.attr("identifier-prefixes") { - ns.identifier_prefixes = s.split(',').map(String::from).collect(); + if let Some(s) = attr!(elem, b"identifier-prefixes") { + ns.identifier_prefixes = attr_value!(s).split(',').map(String::from).collect(); } - if let Some(s) = elem.attr("symbol-prefixes") { - ns.symbol_prefixes = s.split(',').map(String::from).collect(); + if let Some(s) = attr!(elem, b"symbol-prefixes") { + ns.symbol_prefixes = attr_value!(s).split(',').map(String::from).collect(); } } trace!( "Reading {}-{}", ns_name, - elem.attr("version").unwrap_or("?") + attr!(elem, b"version").map(|a| attr_value!(a)).unwrap_or("?"), ); - parser.elements(|parser, elem| { - trace!("<{} name={:?}>", elem.name(), elem.attr("name")); + elements!(parser, buf, elem, { + trace!( + "<{} name={:?}>", + from_utf8!(elem.name()), + attr!(elem, b"name").map(|a| attr_value!(a)), + ); match elem.name() { - "class" => self.read_class(parser, ns_id, elem), - "record" => self.read_record_start(parser, ns_id, elem), - "union" => self.read_named_union(parser, ns_id, elem), - "interface" => self.read_interface(parser, ns_id, elem), - "callback" => self.read_named_callback(parser, ns_id, elem), - "bitfield" => self.read_bitfield(parser, ns_id, elem), - "enumeration" => self.read_enumeration(parser, ns_id, elem), - "function" => self.read_global_function(parser, ns_id, elem), - "constant" => self.read_constant(parser, ns_id, elem), - "alias" => self.read_alias(parser, ns_id, elem), - "function-macro" | "docsection" => parser.ignore_element(), + b"class" => self.read_class(parser, buf, ns_id, &elem), + b"record" => self.read_record_start(parser, buf, ns_id, &elem), + b"union" => self.read_named_union(parser, buf, ns_id, &elem), + b"interface" => self.read_interface(parser, buf, ns_id, &elem), + b"callback" => self.read_named_callback(parser, buf, ns_id, &elem), + b"bitfield" => self.read_bitfield(parser, buf, ns_id, &elem), + b"enumeration" => self.read_enumeration(parser, buf, ns_id, &elem), + b"function" => self.read_global_function(parser, buf, ns_id, &elem), + b"constant" => self.read_constant(parser, buf, ns_id, &elem), + b"alias" => self.read_alias(parser, buf, ns_id, &elem), + b"function-macro" | b"docsection" => parser.ignore_element(buf), _ => { - warn!("<{} name={:?}>", elem.name(), elem.attr("name")); - parser.ignore_element() + warn!( + "<{} name={:?}>", + from_utf8!(elem.name()), + attr!(elem, b"name").map(|a| attr_value!(a)), + ); + parser.ignore_element(buf) } } - })?; + }); Ok(()) } - fn read_class( + fn read_class<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result<(), String> { - let class_name = elem.attr_required("name")?; - let c_type = self.read_object_c_type(parser, elem)?; - let symbol_prefix = elem.attr_required("symbol-prefix").map(ToOwned::to_owned)?; - let type_struct = elem.attr("type-struct").map(ToOwned::to_owned); - let get_type = elem.attr_required("get-type")?; + let class_name = attr_or_err!(elem, b"name")?; + let class_name = attr_value!(class_name); + let c_type = read_object_c_type!(parser, elem)?; + let c_type = attr_value!(c_type); + let symbol_prefix = attr_or_err!(elem, b"symbol-prefix").map(|a| attr_value!(a).to_owned())?; + let type_struct = attr!(elem, b"type-struct").map(|a| attr_value!(a).to_owned()); + let get_type = attr_or_err!(elem, b"get-type")?; let version = self.read_version(parser, ns_id, elem)?; let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?; - let is_abstract = elem.attr("abstract").map(|x| x == "1").unwrap_or(false); + let is_abstract = attr!(elem, b"abstract").map(|x| &*x.value == b"1").unwrap_or(false); let mut fns = Vec::new(); let mut signals = Vec::new(); @@ -163,68 +292,70 @@ impl Library { let mut doc_deprecated = None; let mut union_count = 1; - parser.elements(|parser, elem| match elem.name() { - "constructor" | "function" | "method" => { - self.read_function_to_vec(parser, ns_id, elem, &mut fns) - } - "implements" => self.read_type(parser, ns_id, elem).map(|r| { - impls.push(r.0); - }), - "signal" => self - .read_signal(parser, ns_id, elem) - .map(|s| signals.push(s)), - "property" => self.read_property(parser, ns_id, elem).map(|p| { - if let Some(p) = p { - properties.push(p); + elements!(parser, buf, elem, { + match elem.name() { + b"constructor" | b"function" | b"method" => { + self.read_function_to_vec(parser, buf, ns_id, &elem, &mut fns) } - }), - "field" => self.read_field(parser, ns_id, elem).map(|f| { - fields.push(f); - }), - "virtual-method" => parser.ignore_element(), - "doc" => parser.text().map(|t| doc = Some(t)), - "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)), - "source-position" => parser.ignore_element(), - "union" => self - .read_union(parser, ns_id, elem, Some(class_name), Some(c_type)) - .map(|mut u| { - let field_name = if let Some(field_name) = elem.attr("name") { - field_name.into() - } else { - format!("u{}", union_count) - }; - - u = Union { - name: format!("{}_{}", class_name, field_name), - c_type: Some(format!("{}_{}", c_type, field_name)), - ..u - }; - - let u_doc = u.doc.clone(); - let ctype = u.c_type.clone(); - - fields.push(Field { - name: field_name, - typ: Type::union(self, u, ns_id), - doc: u_doc, - c_type: ctype, - ..Field::default() - }); - union_count += 1; + b"implements" => self.read_type(parser, buf, ns_id, &elem).map(|r| { + impls.push(r.0); + }), + b"signal" => self + .read_signal(parser, buf, ns_id, &elem) + .map(|s| signals.push(s)), + b"property" => self.read_property(parser, buf, ns_id, &elem).map(|p| { + if let Some(p) = p { + properties.push(p); + } + }), + b"field" => self.read_field(parser, buf, ns_id, &elem).map(|f| { + fields.push(f); }), - "attribute" => parser.ignore_element(), - _ => Err(parser.unexpected_element(elem)), - })?; + b"virtual-method" => parser.ignore_element(buf), + b"doc" => parser.text(buf, b"doc").map(|t| doc = Some(t)), + b"doc-deprecated" => parser + .text(buf, b"doc-deprecated") + .map(|t| doc_deprecated = Some(t)), + b"source-position" => parser.ignore_element(buf), + b"union" => self + .read_union(parser, buf, ns_id, &elem, Some(class_name), Some(c_type)) + .map(|mut u| { + let field_name = if let Some(field_name) = attr!(elem, b"name") { + attr_value!(field_name).into() + } else { + format!("u{}", union_count) + }; + + u = Union { + name: format!("{}_{}", class_name, field_name), + c_type: Some(format!("{}_{}", c_type, field_name)), + ..u + }; + + let u_doc = u.doc.clone(); + let ctype = u.c_type.clone(); + + fields.push(Field { + name: field_name, + typ: Type::union(self, u, ns_id), + doc: u_doc, + c_type: ctype, + ..Field::default() + }); + union_count += 1; + }), + b"attribute" => parser.ignore_element(buf), + _ => return Err(parser.unexpected_element(&elem)), + } + }); - let parent = elem - .attr("parent") - .map(|s| self.find_or_stub_type(ns_id, s)); + let parent = attr!(elem, b"parent").map(|s| self.find_or_stub_type(ns_id, attr_value!(s))); let typ = Type::Class(Class { name: class_name.into(), c_type: c_type.into(), type_struct, c_class_type: None, // this will be resolved during postprocessing - glib_get_type: get_type.into(), + glib_get_type: attr_value!(get_type).into(), fields, functions: fns, signals, @@ -243,35 +374,39 @@ impl Library { Ok(()) } - fn read_record_start( + fn read_record_start<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result<(), String> { - if let Some(typ) = self.read_record(parser, ns_id, elem, None, None)? { + if let Some(typ) = self.read_record(parser, buf, ns_id, elem, None, None)? { let name = typ.get_name(); self.add_type(ns_id, &name, typ); } Ok(()) } - fn read_record( + fn read_record<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, parent_name_prefix: Option<&str>, parent_ctype_prefix: Option<&str>, ) -> Result, String> { - let record_name = elem.attr_required("name")?; - let c_type = elem.attr_required("type")?; - let symbol_prefix = elem.attr("symbol-prefix").map(ToOwned::to_owned); - let get_type = elem.attr("get-type").map(ToOwned::to_owned); - let gtype_struct_for = elem.attr("is-gtype-struct-for"); + let record_name = attr_or_err!(elem, b"name")?; + let record_name = attr_value!(record_name); + let c_type = attr_or_err!(elem, b"type")?; + let c_type = attr_value!(c_type); + let symbol_prefix = attr!(elem, b"symbol-prefix").map(|a| attr_value!(a).to_owned()); + let get_type = attr!(elem, b"get-type").map(|a| attr_value!(a).to_owned()); + let gtype_struct_for = attr!(elem, b"is-gtype-struct-for"); let version = self.read_version(parser, ns_id, elem)?; let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?; - let disguised = elem.attr_bool("disguised", false); + let disguised = attr_bool!(elem, b"disguised", false); let mut fields = Vec::new(); let mut fns = Vec::new(); @@ -279,95 +414,99 @@ impl Library { let mut doc_deprecated = None; let mut union_count = 1; - parser.elements(|parser, elem| match elem.name() { - "constructor" | "function" | "method" => { - self.read_function_to_vec(parser, ns_id, elem, &mut fns) - } - "union" => self - .read_union(parser, ns_id, elem, Some(record_name), Some(c_type)) - .map(|mut u| { - let field_name = if let Some(field_name) = elem.attr("name") { - field_name.into() - } else { - format!("u{}", union_count) - }; - - u = Union { - name: format!( - "{}{}_{}", - parent_name_prefix - .map(|s| { - let mut s = String::from(s); - s.push('_'); - s - }) - .unwrap_or_else(String::new), - record_name, - field_name - ), - c_type: Some(format!( - "{}{}_{}", - parent_ctype_prefix - .map(|s| { - let mut s = String::from(s); - s.push('_'); - s - }) - .unwrap_or_else(String::new), - c_type, - field_name - )), - ..u - }; - - let u_doc = u.doc.clone(); - let ctype = u.c_type.clone(); - - fields.push(Field { - name: field_name, - typ: Type::union(self, u, ns_id), - doc: u_doc, - c_type: ctype, - ..Field::default() - }); - union_count += 1; - }), - "field" => { - self.read_field(parser, ns_id, elem).map(|mut f| { - // Workaround for bitfields - if c_type == "GDate" { - if f.name == "julian_days" { - fields.push(f); - } else if f.name == "julian" { - f.name = "flags_dmy".into(); - f.typ = TypeId::tid_uint32(); - f.c_type = Some("guint".into()); - f.bits = None; - fields.push(f); + elements!(parser, buf, elem, { + match elem.name() { + b"constructor" | b"function" | b"method" => { + self.read_function_to_vec(parser, buf, ns_id, &elem, &mut fns) + } + b"union" => self + .read_union(parser, buf, ns_id, &elem, Some(record_name), Some(c_type)) + .map(|mut u| { + let field_name = if let Some(field_name) = attr!(elem, b"name") { + attr_value!(field_name).into() } else { - // Skip + format!("u{}", union_count) + }; + + u = Union { + name: format!( + "{}{}_{}", + parent_name_prefix + .map(|s| { + let mut s = String::from(s); + s.push('_'); + s + }) + .unwrap_or_else(String::new), + record_name, + field_name + ), + c_type: Some(format!( + "{}{}_{}", + parent_ctype_prefix + .map(|s| { + let mut s = String::from(s); + s.push('_'); + s + }) + .unwrap_or_else(String::new), + c_type, + field_name + )), + ..u + }; + + let u_doc = u.doc.clone(); + let ctype = u.c_type.clone(); + + fields.push(Field { + name: field_name, + typ: Type::union(self, u, ns_id), + doc: u_doc, + c_type: ctype, + ..Field::default() + }); + union_count += 1; + }), + b"field" => { + self.read_field(parser, buf, ns_id, &elem).map(|mut f| { + // Workaround for bitfields + if c_type == "GDate" { + if f.name == "julian_days" { + fields.push(f); + } else if f.name == "julian" { + f.name = "flags_dmy".into(); + f.typ = TypeId::tid_uint32(); + f.c_type = Some("guint".into()); + f.bits = None; + fields.push(f); + } else { + // Skip + } + } else { + // Workaround for wrong GValue c:type + if c_type == "GValue" && f.name == "data" { + f.c_type = Some("GValue_data".into()); + } + fields.push(f); } - return; - } - // Workaround for wrong GValue c:type - if c_type == "GValue" && f.name == "data" { - f.c_type = Some("GValue_data".into()); - } - fields.push(f); - }) + }) + } + b"doc" => parser.text(buf, b"doc").map(|t| doc = Some(t)), + b"doc-deprecated" => parser + .text(buf, b"doc-deprecated") + .map(|t| doc_deprecated = Some(t)), + b"source-position" => parser.ignore_element(buf), + b"attribute" => parser.ignore_element(buf), + _ => return Err(parser.unexpected_element(&elem)), } - "doc" => parser.text().map(|t| doc = Some(t)), - "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)), - "source-position" => parser.ignore_element(), - "attribute" => parser.ignore_element(), - _ => Err(parser.unexpected_element(elem)), - })?; + }); let typ = Type::Record(Record { name: record_name.into(), c_type: c_type.into(), glib_get_type: get_type, - gtype_struct_for: gtype_struct_for.map(|s| s.into()), + gtype_struct_for: gtype_struct_for.map(|s| attr_value!(s).into()), fields, functions: fns, version, @@ -381,23 +520,24 @@ impl Library { Ok(Some(typ)) } - fn read_named_union( + fn read_named_union<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result<(), String> { // Require a name here - elem.attr_required("name")?; + attr_or_err!(elem, b"name")?; - self.read_union(parser, ns_id, elem, None, None) + self.read_union(parser, buf, ns_id, elem, None, None) .and_then(|mut u| { assert_ne!(u.name, ""); // Workaround for missing c:type if u.name == "_Value__data__union" { u.c_type = Some("GValue_data".into()); } else if u.c_type.is_none() { - return Err(parser.fail("Missing union c:type")); + return Err(parser.error("Missing union c:type")); } let union_name = u.name.clone(); self.add_type(ns_id, &union_name, Type::Union(u)); @@ -405,97 +545,99 @@ impl Library { }) } - fn read_union( + fn read_union<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, parent_name_prefix: Option<&str>, parent_ctype_prefix: Option<&str>, ) -> Result { - let union_name = elem.attr("name").unwrap_or(""); - let c_type = self.read_object_c_type(parser, elem).unwrap_or(""); - let get_type = elem.attr("get-type").map(|s| s.into()); - let symbol_prefix = elem.attr("symbol-prefix").map(ToOwned::to_owned); + let union_name = attr!(elem, b"name"); + let union_name = union_name.map(|a| attr_value!(a)).unwrap_or(""); + let c_type = read_object_c_type!(parser, elem); + let c_type = c_type.map(|a| attr_value!(a)).unwrap_or(""); + let get_type = attr!(elem, b"get-type").map(|s| attr_value!(s).into()); + let symbol_prefix = attr!(elem, b"symbol-prefix").map(|a| attr_value!(a).to_owned()); let mut fields = Vec::new(); let mut fns = Vec::new(); let mut doc = None; let mut struct_count = 1; - parser.elements(|parser, elem| match elem.name() { - "source-position" => parser.ignore_element(), - "field" => self.read_field(parser, ns_id, elem).map(|f| { - fields.push(f); - }), - "constructor" | "function" | "method" => { - self.read_function_to_vec(parser, ns_id, elem, &mut fns) - } - "record" => { - let mut r = match self.read_record( - parser, - ns_id, - elem, - parent_name_prefix, - parent_ctype_prefix, - )? { - Some(Type::Record(r)) => r, - _ => return Ok(()), - }; - - let field_name = if let Some(field_name) = elem.attr("name") { - field_name.into() - } else { - format!("s{}", struct_count) - }; - - r = Record { - name: format!( - "{}{}_{}", - parent_name_prefix - .map(|s| { - let mut s = String::from(s); - s.push('_'); - s - }) - .unwrap_or_else(String::new), - union_name, - field_name - ), - c_type: format!( - "{}{}_{}", - parent_ctype_prefix - .map(|s| { - let mut s = String::from(s); - s.push('_'); - s - }) - .unwrap_or_else(String::new), - c_type, - field_name - ), - ..r - }; - - let r_doc = r.doc.clone(); - let ctype = r.c_type.clone(); - - fields.push(Field { - name: field_name, - typ: Type::record(self, r, ns_id), - doc: r_doc, - c_type: Some(ctype), - ..Field::default() - }); - - struct_count += 1; - - Ok(()) + elements!(parser, buf, elem, { + match elem.name() { + b"source-position" => parser.ignore_element(buf), + b"field" => self.read_field(parser, buf, ns_id, &elem).map(|f| { + fields.push(f); + }), + b"constructor" | b"function" | b"method" => { + self.read_function_to_vec(parser, buf, ns_id, &elem, &mut fns) + } + b"record" => { + if let Some(Type::Record(mut r)) = self.read_record( + parser, + buf, + ns_id, + &elem, + parent_name_prefix, + parent_ctype_prefix, + )? { + let field_name = if let Some(field_name) = attr!(elem, b"name") { + attr_value!(field_name).into() + } else { + format!("s{}", struct_count) + }; + + r = Record { + name: format!( + "{}{}_{}", + parent_name_prefix + .map(|s| { + let mut s = String::from(s); + s.push('_'); + s + }) + .unwrap_or_else(String::new), + union_name, + field_name + ), + c_type: format!( + "{}{}_{}", + parent_ctype_prefix + .map(|s| { + let mut s = String::from(s); + s.push('_'); + s + }) + .unwrap_or_else(String::new), + c_type, + field_name + ), + ..r + }; + + let r_doc = r.doc.clone(); + let ctype = r.c_type.clone(); + + fields.push(Field { + name: field_name, + typ: Type::record(self, r, ns_id), + doc: r_doc, + c_type: Some(ctype), + ..Field::default() + }); + + struct_count += 1; + } + Ok(()) + } + b"doc" => parser.text(buf, b"doc").map(|t| doc = Some(t)), + b"attribute" => parser.ignore_element(buf), + _ => return Err(parser.unexpected_element(&elem)), } - "doc" => parser.text().map(|t| doc = Some(t)), - "attribute" => parser.ignore_element(), - _ => Err(parser.unexpected_element(elem)), - })?; + }); Ok(Union { name: union_name.into(), @@ -508,45 +650,48 @@ impl Library { }) } - fn read_field( + fn read_field<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result { - let field_name = elem.attr_required("name")?; - let private = elem.attr_bool("private", false); - let bits = elem.attr("bits").and_then(|s| s.parse().ok()); + let field_name = attr_or_err!(elem, b"name")?; + let private = attr_bool!(elem, b"private", false); + let bits = attr!(elem, b"bits").and_then(|s| attr_value!(s).parse().ok()); let mut typ = None; let mut doc = None; - parser.elements(|parser, elem| match elem.name() { - "type" | "array" => { - if typ.is_some() { - return Err(parser.fail("Too many elements")); + elements!(parser, buf, elem, { + match elem.name() { + b"type" | b"array" => { + if typ.is_some() { + return Err(parser.error("Too many elements")); + } + self.read_type(parser, buf, ns_id, &elem).map(|t| { + typ = Some(t); + }) } - self.read_type(parser, ns_id, elem).map(|t| { - typ = Some(t); - }) - } - "callback" => { - if typ.is_some() { - return Err(parser.fail("Too many elements")); + b"callback" => { + if typ.is_some() { + return Err(parser.error("Too many elements")); + } + self.read_function(parser, buf, ns_id, elem.name(), &elem) + .map(|f| { + typ = Some((Type::function(self, f), None, None)); + }) } - self.read_function(parser, ns_id, elem.name(), elem) - .map(|f| { - typ = Some((Type::function(self, f), None, None)); - }) + b"doc" => parser.text(buf, b"doc").map(|t| doc = Some(t)), + b"attribute" => parser.ignore_element(buf), + _ => return Err(parser.unexpected_element(&elem)), } - "doc" => parser.text().map(|t| doc = Some(t)), - "attribute" => parser.ignore_element(), - _ => Err(parser.unexpected_element(elem)), - })?; + }); if let Some((tid, c_type, array_length)) = typ { Ok(Field { - name: field_name.into(), + name: attr_value!(field_name).into(), typ: tid, c_type, private, @@ -555,17 +700,18 @@ impl Library { doc, }) } else { - Err(parser.fail("Missing element")) + Err(parser.error("Missing element")) } } - fn read_named_callback( + fn read_named_callback<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result<(), String> { - self.read_function_if_not_moved(parser, ns_id, elem.name(), elem)? + self.read_function_if_not_moved(parser, buf, ns_id, elem.name(), elem)? .map(|func| { let name = func.name.clone(); self.add_type(ns_id, &name, Type::Function(func)) @@ -574,17 +720,19 @@ impl Library { Ok(()) } - fn read_interface( + fn read_interface<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result<(), String> { - let interface_name = elem.attr_required("name")?; - let c_type = self.read_object_c_type(parser, elem)?; - let symbol_prefix = elem.attr_required("symbol-prefix").map(ToOwned::to_owned)?; - let type_struct = elem.attr("type-struct").map(ToOwned::to_owned); - let get_type = elem.attr_required("get-type")?; + let interface_name = attr_or_err!(elem, b"name")?; + let interface_name = attr_value!(interface_name); + let c_type = read_object_c_type!(parser, elem)?; + let symbol_prefix = attr_or_err!(elem, b"symbol-prefix").map(|a| attr_value!(a).to_owned())?; + let type_struct = attr!(elem, b"type-struct").map(|a| attr_value!(a).to_owned()); + let get_type = attr_or_err!(elem, b"get-type")?; let version = self.read_version(parser, ns_id, elem)?; let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?; @@ -595,35 +743,39 @@ impl Library { let mut doc = None; let mut doc_deprecated = None; - parser.elements(|parser, elem| match elem.name() { - "constructor" | "function" | "method" => { - self.read_function_to_vec(parser, ns_id, elem, &mut fns) - } - "prerequisite" => self.read_type(parser, ns_id, elem).map(|r| { - prereqs.push(r.0); - }), - "signal" => self - .read_signal(parser, ns_id, elem) - .map(|s| signals.push(s)), - "property" => self.read_property(parser, ns_id, elem).map(|p| { - if let Some(p) = p { - properties.push(p); + elements!(parser, buf, elem, { + match elem.name() { + b"constructor" | b"function" | b"method" => { + self.read_function_to_vec(parser, buf, ns_id, &elem, &mut fns) } - }), - "doc" => parser.text().map(|t| doc = Some(t)), - "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)), - "virtual-method" => parser.ignore_element(), - "source-position" => parser.ignore_element(), - "attribute" => parser.ignore_element(), - _ => Err(parser.unexpected_element(elem)), - })?; + b"prerequisite" => self.read_type(parser, buf, ns_id, &elem).map(|r| { + prereqs.push(r.0); + }), + b"signal" => self + .read_signal(parser, buf, ns_id, &elem) + .map(|s| signals.push(s)), + b"property" => self.read_property(parser, buf, ns_id, &elem).map(|p| { + if let Some(p) = p { + properties.push(p); + } + }), + b"doc" => parser.text(buf, b"doc").map(|t| doc = Some(t)), + b"doc-deprecated" => parser + .text(buf, b"doc-deprecated") + .map(|t| doc_deprecated = Some(t)), + b"virtual-method" => parser.ignore_element(buf), + b"source-position" => parser.ignore_element(buf), + b"attribute" => parser.ignore_element(buf), + _ => return Err(parser.unexpected_element(&elem)), + } + }); let typ = Type::Interface(Interface { name: interface_name.into(), - c_type: c_type.into(), + c_type: attr_value!(c_type).into(), type_struct, c_class_type: None, // this will be resolved during postprocessing - glib_get_type: get_type.into(), + glib_get_type: attr_value!(get_type).into(), functions: fns, signals, properties, @@ -638,16 +790,20 @@ impl Library { Ok(()) } - fn read_bitfield( + fn read_bitfield<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result<(), String> { - let bitfield_name = elem.attr_required("name")?; - let c_type = self.read_object_c_type(parser, elem)?; - let symbol_prefix = elem.attr("symbol-prefix").map(ToOwned::to_owned); - let get_type = elem.attr("get-type").map(|s| s.into()); + let bitfield_name = attr_or_err!(elem, b"name")?; + let bitfield_name = attr_value!(bitfield_name); + let c_type = read_object_c_type!(parser, elem)?; + let symbol_prefix = attr_or_err!(elem, b"symbol-prefix") + .map(|a| attr_value!(a).to_owned()) + .ok(); + let get_type = attr!(elem, b"get-type").map(|s| attr_value!(s).into()); let version = self.read_version(parser, ns_id, elem)?; let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?; @@ -656,23 +812,27 @@ impl Library { let mut doc = None; let mut doc_deprecated = None; - parser.elements(|parser, elem| match elem.name() { - "member" => self - .read_member(parser, ns_id, elem) - .map(|m| members.push(m)), - "constructor" | "function" | "method" => { - self.read_function_to_vec(parser, ns_id, elem, &mut fns) + elements!(parser, buf, elem, { + match elem.name() { + b"member" => self + .read_member(parser, buf, ns_id, &elem) + .map(|m| members.push(m)), + b"constructor" | b"function" | b"method" => { + self.read_function_to_vec(parser, buf, ns_id, &elem, &mut fns) + } + b"doc" => parser.text(buf, b"doc").map(|t| doc = Some(t)), + b"doc-deprecated" => parser + .text(buf, b"doc-deprecated") + .map(|t| doc_deprecated = Some(t)), + b"source-position" => parser.ignore_element(buf), + b"attribute" => parser.ignore_element(buf), + _ => return Err(parser.unexpected_element(&elem)), } - "doc" => parser.text().map(|t| doc = Some(t)), - "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)), - "source-position" => parser.ignore_element(), - "attribute" => parser.ignore_element(), - _ => Err(parser.unexpected_element(elem)), - })?; + }); let typ = Type::Bitfield(Bitfield { name: bitfield_name.into(), - c_type: c_type.into(), + c_type: attr_value!(c_type).into(), members, functions: fns, version, @@ -686,44 +846,49 @@ impl Library { Ok(()) } - fn read_enumeration( + fn read_enumeration<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result<(), String> { - let enum_name = elem.attr_required("name")?; - let c_type = self.read_object_c_type(parser, elem)?; - let symbol_prefix = elem.attr("symbol-prefix").map(ToOwned::to_owned); - let get_type = elem.attr("get-type").map(|s| s.into()); + let enum_name = attr_or_err!(elem, b"name")?; + let enum_name = attr_value!(enum_name); + let c_type = read_object_c_type!(parser, elem)?; + let symbol_prefix = attr!(elem, b"symbol-prefix").map(|a| attr_value!(a).to_owned()); + let get_type = attr!(elem, b"get-type").map(|s| attr_value!(s).into()); let version = self.read_version(parser, ns_id, elem)?; let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?; - let error_domain = elem - .attr("error-domain") - .map(|s| ErrorDomain::Quark(String::from(s))); + let error_domain = + attr!(elem, b"error-domain").map(|s| ErrorDomain::Quark(String::from(attr_value!(s)))); let mut members = Vec::new(); let mut fns = Vec::new(); let mut doc = None; let mut doc_deprecated = None; - parser.elements(|parser, elem| match elem.name() { - "member" => self - .read_member(parser, ns_id, elem) - .map(|m| members.push(m)), - "constructor" | "function" | "method" => { - self.read_function_to_vec(parser, ns_id, elem, &mut fns) + elements!(parser, buf, elem, { + match elem.name() { + b"member" => self + .read_member(parser, buf, ns_id, &elem) + .map(|m| members.push(m)), + b"constructor" | b"function" | b"method" => { + self.read_function_to_vec(parser, buf, ns_id, &elem, &mut fns) + } + b"doc" => parser.text(buf, b"doc").map(|t| doc = Some(t)), + b"doc-deprecated" => parser + .text(buf, b"doc-deprecated") + .map(|t| doc_deprecated = Some(t)), + b"source-position" => parser.ignore_element(buf), + b"attribute" => parser.ignore_element(buf), + _ => return Err(parser.unexpected_element(&elem)), } - "doc" => parser.text().map(|t| doc = Some(t)), - "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)), - "source-position" => parser.ignore_element(), - "attribute" => parser.ignore_element(), - _ => Err(parser.unexpected_element(elem)), - })?; + }); let typ = Type::Enumeration(Enumeration { name: enum_name.into(), - c_type: c_type.into(), + c_type: attr_value!(c_type).into(), members, functions: fns, version, @@ -738,13 +903,14 @@ impl Library { Ok(()) } - fn read_global_function( + fn read_global_function<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result<(), String> { - self.read_function_if_not_moved(parser, ns_id, "global", elem) + self.read_function_if_not_moved(parser, buf, ns_id, b"global", elem) .map(|func| { if let Some(func) = func { self.add_function(ns_id, func); @@ -752,15 +918,16 @@ impl Library { }) } - fn read_constant( + fn read_constant<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result<(), String> { - let const_name = elem.attr_required("name")?; - let c_identifier = elem.attr_required("type")?; - let value = elem.attr_required("value")?; + let const_name = attr_or_err!(elem, b"name")?; + let c_identifier = attr_or_err!(elem, b"type")?; + let value = attr_or_err!(elem, b"value")?; let version = self.read_version(parser, ns_id, elem)?; let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?; @@ -768,41 +935,43 @@ impl Library { let mut doc = None; let mut doc_deprecated = None; - parser.elements(|parser, elem| match elem.name() { - "type" | "array" => { - if inner.is_some() { - return Err(parser.fail_with_position( - "Too many inner elements in element", - elem.position(), - )); - } - let (typ, c_type, array_length) = self.read_type(parser, ns_id, elem)?; - if let Some(c_type) = c_type { - inner = Some((typ, c_type, array_length)); - } else { - return Err(parser.fail_with_position( - "Missing element's c:type", - elem.position(), - )); + elements!(parser, buf, elem, { + match elem.name() { + b"type" | b"array" => { + if inner.is_some() { + return Err(parser.error_with_position( + "Too many inner elements in element", + )); + } + let (typ, c_type, array_length) = self.read_type(parser, buf, ns_id, &elem)?; + if let Some(c_type) = c_type { + inner = Some((typ, c_type, array_length)); + } else { + return Err( + parser.error_with_position("Missing element's c:type") + ); + } + Ok(()) } - Ok(()) + b"doc" => parser.text(buf, b"doc").map(|t| doc = Some(t)), + b"doc-deprecated" => parser + .text(buf, b"doc-deprecated") + .map(|t| doc_deprecated = Some(t)), + b"source-position" => parser.ignore_element(buf), + b"attribute" => parser.ignore_element(buf), + _ => return Err(parser.unexpected_element(&elem)), } - "doc" => parser.text().map(|t| doc = Some(t)), - "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)), - "source-position" => parser.ignore_element(), - "attribute" => parser.ignore_element(), - _ => Err(parser.unexpected_element(elem)), - })?; + }); if let Some((typ, c_type, _array_length)) = inner { self.add_constant( ns_id, Constant { - name: const_name.into(), - c_identifier: c_identifier.into(), + name: attr_value!(const_name).into(), + c_identifier: attr_value!(c_identifier).into(), typ, c_type, - value: value.into(), + value: attr_value!(value).into(), version, deprecated_version, doc, @@ -811,53 +980,55 @@ impl Library { ); Ok(()) } else { - Err(parser.fail_with_position( - "Missing element inside element", - elem.position(), - )) + Err(parser.error_with_position("Missing element inside element")) } } - fn read_alias( + fn read_alias<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result<(), String> { - let alias_name = elem.attr_required("name")?; - let c_identifier = elem.attr_required("type")?; + let alias_name = attr_or_err!(elem, b"name")?; + let alias_name = attr_value!(alias_name); + let c_identifier = attr_or_err!(elem, b"type")?; let mut inner = None; let mut doc = None; let mut doc_deprecated = None; - parser.elements(|parser, elem| match elem.name() { - "source-position" => parser.ignore_element(), - "type" | "array" => { - if inner.is_some() { - return Err(parser.fail_with_position( - "Too many inner elements in element", - elem.position(), - )); - } - let (typ, c_type, array_length) = self.read_type(parser, ns_id, elem)?; - if let Some(c_type) = c_type { - inner = Some((typ, c_type, array_length)); - } else { - return Err(parser.fail("Missing target's c:type")); + elements!(parser, buf, elem, { + match elem.name() { + b"source-position" => parser.ignore_element(buf), + b"type" | b"array" => { + if inner.is_some() { + return Err(parser.error_with_position( + "Too many inner elements in element", + )); + } + let (typ, c_type, array_length) = self.read_type(parser, buf, ns_id, &elem)?; + if let Some(c_type) = c_type { + inner = Some((typ, c_type, array_length)); + } else { + return Err(parser.error("Missing target's c:type")); + } + Ok(()) } - Ok(()) + b"doc" => parser.text(buf, b"doc").map(|t| doc = Some(t)), + b"doc-deprecated" => parser + .text(buf, b"doc-deprecated") + .map(|t| doc_deprecated = Some(t)), + b"attribute" => parser.ignore_element(buf), + _ => return Err(parser.unexpected_element(&elem)), } - "doc" => parser.text().map(|t| doc = Some(t)), - "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)), - "attribute" => parser.ignore_element(), - _ => Err(parser.unexpected_element(elem)), - })?; + }); if let Some((typ, c_type, _array_length)) = inner { let typ = Type::Alias(Alias { name: alias_name.into(), - c_identifier: c_identifier.into(), + c_identifier: attr_value!(c_identifier).into(), typ, target_c_type: c_type, doc, @@ -866,32 +1037,34 @@ impl Library { self.add_type(ns_id, alias_name, typ); Ok(()) } else { - Err(parser.fail_with_position( - "Missing element inside element", - elem.position(), - )) + Err(parser.error_with_position("Missing element inside element")) } } - fn read_member( + fn read_member<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result { - let member_name = elem.attr_required("name")?; - let value = elem.attr_required("value")?; - let c_identifier = elem.attr("identifier").map(|x| x.into()); + let member_name = attr_or_err!(elem, b"name")?; + let member_name = attr_value!(member_name); + let value = attr_or_err!(elem, b"value")?; + let value = attr_value!(value); + let c_identifier = attr!(elem, b"identifier").map(|x| attr_value!(x).into()); let version = self.read_version(parser, ns_id, elem)?; let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?; let mut doc = None; - parser.elements(|parser, elem| match elem.name() { - "doc" => parser.text().map(|t| doc = Some(t)), - "attribute" => parser.ignore_element(), - _ => Err(parser.unexpected_element(elem)), - })?; + elements!(parser, buf, elem, { + match elem.name() { + b"doc" => parser.text(buf, b"doc").map(|t| doc = Some(t)), + b"attribute" => parser.ignore_element(buf), + _ => return Err(parser.unexpected_element(&elem)), + } + }); Ok(Member { name: member_name.into(), @@ -904,16 +1077,18 @@ impl Library { }) } - fn read_function( + fn read_function<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - kind_str: &str, - elem: &Element, + kind: &[u8], + elem: &BytesStart<'_>, ) -> Result { - let fn_name = elem.attr_required("name")?; - let c_identifier = elem.attr("identifier").or_else(|| elem.attr("type")); - let kind = FunctionKind::from_str(kind_str).map_err(|why| parser.fail(&why))?; + let fn_name = attr_or_err!(elem, b"name")?; + let c_identifier = attr!(elem, b"identifier").or_else(|| attr!(elem, b"type")); + let kind_str = from_utf8!(kind); + let kind = FunctionKind::from_str(kind_str).map_err(|why| parser.error(&why))?; let is_method = kind == FunctionKind::Method; let version = self.read_version(parser, ns_id, elem)?; let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?; @@ -923,29 +1098,32 @@ impl Library { let mut doc = None; let mut doc_deprecated = None; - parser.elements(|parser, elem| match elem.name() { - "parameters" => self - .read_parameters(parser, ns_id, false, is_method) - .map(|mut ps| params.append(&mut ps)), - "return-value" => { - if ret.is_some() { - return Err(parser.fail_with_position( - "Too many elements inside element", - elem.position(), - )); + elements!(parser, buf, elem, { + match elem.name() { + b"parameters" => self + .read_parameters(parser, buf, ns_id, false, is_method) + .map(|mut ps| params.append(&mut ps)), + b"return-value" => { + if ret.is_some() { + return Err(parser.error_with_position( + "Too many elements inside element", + )); + } + ret = Some(self.read_parameter(parser, buf, ns_id, &elem, false, is_method)?); + Ok(()) } - ret = Some(self.read_parameter(parser, ns_id, elem, false, is_method)?); - Ok(()) + b"doc" => parser.text(buf, b"doc").map(|t| doc = Some(t)), + b"doc-deprecated" => parser + .text(buf, b"doc-deprecated") + .map(|t| doc_deprecated = Some(t)), + b"doc-version" => parser.ignore_element(buf), + b"source-position" => parser.ignore_element(buf), + b"attribute" => parser.ignore_element(buf), + _ => return Err(parser.unexpected_element(&elem)), } - "doc" => parser.text().map(|t| doc = Some(t)), - "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)), - "doc-version" => parser.ignore_element(), - "source-position" => parser.ignore_element(), - "attribute" => parser.ignore_element(), - _ => Err(parser.unexpected_element(elem)), - })?; - - let throws = elem.attr_bool("throws", false); + }); + + let throws = attr_bool!(elem, b"throws", false); if throws { params.push(Parameter { name: "error".into(), @@ -967,8 +1145,8 @@ impl Library { } if let Some(ret) = ret { Ok(Function { - name: fn_name.into(), - c_identifier: c_identifier.map(|s| s.into()), + name: attr_value!(fn_name).into(), + c_identifier: c_identifier.map(|s| attr_value!(s).into()), kind, parameters: params, ret, @@ -979,60 +1157,57 @@ impl Library { doc_deprecated, }) } else { - Err(parser.fail_with_position( - "Missing element in element", - elem.position(), - )) + Err(parser.error_with_position("Missing element in element")) } } - fn read_function_to_vec( + fn read_function_to_vec<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, fns: &mut Vec, ) -> Result<(), String> { - if let Some(f) = self.read_function_if_not_moved(parser, ns_id, elem.name(), elem)? { + if let Some(f) = self.read_function_if_not_moved(parser, buf, ns_id, elem.name(), elem)? { fns.push(f) } Ok(()) } - fn read_function_if_not_moved( + fn read_function_if_not_moved<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - kind_str: &str, - elem: &Element, + kind_str: &[u8], + elem: &BytesStart<'_>, ) -> Result, String> { - if elem.attr("moved-to").is_some() { - return parser.ignore_element().map(|_| None); + if attr!(elem, b"moved-to").is_some() { + return parser.ignore_element(buf).map(|_| None); } - self.read_function(parser, ns_id, kind_str, elem) + self.read_function(parser, buf, ns_id, kind_str, elem) .and_then(|f| { if f.c_identifier.is_none() { - return Err(parser.fail_with_position( - &format!( - "Missing c:identifier attribute in <{}> element", - elem.name() - ), - elem.position(), - )); + return Err(parser.error_with_position(&format!( + "Missing c:identifier attribute in <{}> element", + from_utf8!(elem.name()), + ))); } Ok(Some(f)) }) } - fn read_signal( + fn read_signal<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result { - let signal_name = elem.attr_required("name")?; - let is_action = elem.attr_bool("action", false); - let is_detailed = elem.attr_bool("detailed", false); + let signal_name = attr_or_err!(elem, b"name")?; + let is_action = attr_bool!(elem, b"action", false); + let is_detailed = attr_bool!(elem, b"detailed", false); let version = self.read_version(parser, ns_id, elem)?; let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?; @@ -1041,28 +1216,31 @@ impl Library { let mut doc = None; let mut doc_deprecated = None; - parser.elements(|parser, elem| match elem.name() { - "parameters" => self - .read_parameters(parser, ns_id, true, false) - .map(|mut ps| params.append(&mut ps)), - "return-value" => { - if ret.is_some() { - return Err(parser.fail_with_position( - "Too many elements in element", - elem.position(), - )); + elements!(parser, buf, elem, { + match elem.name() { + b"parameters" => self + .read_parameters(parser, buf, ns_id, true, false) + .map(|mut ps| params.append(&mut ps)), + b"return-value" => { + if ret.is_some() { + return Err(parser.error_with_position( + "Too many elements in element", + )); + } + self.read_parameter(parser, buf, ns_id, &elem, true, false) + .map(|p| ret = Some(p)) } - self.read_parameter(parser, ns_id, elem, true, false) - .map(|p| ret = Some(p)) + b"doc" => parser.text(buf, b"doc").map(|t| doc = Some(t)), + b"doc-deprecated" => parser + .text(buf, b"doc-deprecated") + .map(|t| doc_deprecated = Some(t)), + b"attribute" => parser.ignore_element(buf), + _ => return Err(parser.unexpected_element(&elem)), } - "doc" => parser.text().map(|t| doc = Some(t)), - "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)), - "attribute" => parser.ignore_element(), - _ => Err(parser.unexpected_element(elem)), - })?; + }); if let Some(ret) = ret { Ok(Signal { - name: signal_name.into(), + name: attr_value!(signal_name).into(), parameters: params, ret, is_action, @@ -1073,87 +1251,94 @@ impl Library { doc_deprecated, }) } else { - Err(parser.fail_with_position( - "Missing element in element", - elem.position(), - )) + Err(parser.error_with_position("Missing element in element")) } } - fn read_parameters( + fn read_parameters<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, allow_no_ctype: bool, for_method: bool, ) -> Result, String> { - parser.elements(|parser, elem| match elem.name() { - "parameter" | "instance-parameter" => { - self.read_parameter(parser, ns_id, elem, allow_no_ctype, for_method) + Ok(elements_vec!(parser, buf, elem, { + match elem.name() { + b"parameter" | b"instance-parameter" => { + self.read_parameter(parser, buf, ns_id, &elem, allow_no_ctype, for_method) + } + _ => return Err(parser.unexpected_element(&elem)), } - _ => Err(parser.unexpected_element(elem)), - }) + })) } - fn read_parameter( + fn read_parameter<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, allow_no_ctype: bool, for_method: bool, ) -> Result { - let param_name = elem.attr("name").unwrap_or(""); - let instance_parameter = elem.name() == "instance-parameter"; - let transfer = elem - .attr_from_str("transfer-ownership")? + let param_name = attr!(elem, b"name"); + let param_name = param_name.map(|a| attr_value!(a)).unwrap_or(""); + let instance_parameter = elem.name() == b"instance-parameter"; + let transfer = parser + .attr_from_str(elem, b"transfer-ownership")? .unwrap_or(Transfer::None); - let nullable = elem.attr_bool("nullable", false); - let allow_none = elem.attr_bool("allow-none", false); - let scope = elem.attr_from_str("scope")?.unwrap_or(ParameterScope::None); - let closure = elem.attr_from_str("closure")?; - let destroy = elem.attr_from_str("destroy")?; - let caller_allocates = elem.attr_bool("caller-allocates", false); - let direction = if elem.name() == "return-value" { + let nullable = attr_bool!(elem, b"nullable", false); + let allow_none = attr_bool!(elem, b"allow-none", false); + let scope = parser + .attr_from_str(elem, b"scope")? + .unwrap_or(ParameterScope::None); + let closure = parser.attr_from_str(elem, b"closure")?; + let destroy = parser.attr_from_str(elem, b"destroy")?; + let caller_allocates = attr_bool!(elem, b"caller-allocates", false); + let direction = if elem.name() == b"return-value" { Ok(ParameterDirection::Return) } else { - ParameterDirection::from_str(elem.attr("direction").unwrap_or("in")) - .map_err(|why| parser.fail_with_position(&why, elem.position())) + let direction = attr!(elem, b"direction"); + ParameterDirection::from_str(direction.map(|a| attr_value!(a)).unwrap_or("in")) + .map_err(|why| parser.error_with_position(&why)) }?; let mut typ = None; let mut varargs = false; let mut doc = None; - parser.elements(|parser, elem| match elem.name() { - "type" | "array" => { - if typ.is_some() { - return Err(parser.fail_with_position( - &format!("Too many elements in <{}> element", elem.name()), - elem.position(), - )); - } - typ = Some(self.read_type(parser, ns_id, elem)?); - if let Some((tid, None, _)) = typ { - if allow_no_ctype { - typ = Some((tid, Some(EMPTY_CTYPE.to_owned()), None)); - } else { - return Err(parser.fail_with_position( - &format!("Missing c:type attribute in <{}> element", elem.name()), - elem.position(), - )); + elements!(parser, buf, elem, { + match elem.name() { + b"type" | b"array" => { + if typ.is_some() { + return Err(parser.error_with_position(&format!( + "Too many elements in <{}> element", + from_utf8!(elem.name()), + ))); + } + typ = Some(self.read_type(parser, buf, ns_id, &elem)?); + if let Some((tid, None, _)) = typ { + if allow_no_ctype { + typ = Some((tid, Some(EMPTY_CTYPE.to_owned()), None)); + } else { + return Err(parser.error_with_position(&format!( + "Missing c:type attribute in <{}> element", + from_utf8!(elem.name()), + ))); + } } + Ok(()) } - Ok(()) - } - "varargs" => { - varargs = true; - parser.ignore_element() + b"varargs" => { + varargs = true; + parser.ignore_element(buf) + } + b"doc" => parser.text(buf, b"doc").map(|t| doc = Some(t)), + b"attribute" => parser.ignore_element(buf), + _ => return Err(parser.unexpected_element(&elem)), } - "doc" => parser.text().map(|t| doc = Some(t)), - "attribute" => parser.ignore_element(), - _ => Err(parser.unexpected_element(elem)), - })?; + }); if let Some((tid, c_type, mut array_length)) = typ { if for_method { @@ -1195,26 +1380,28 @@ impl Library { destroy, }) } else { - Err(parser.fail_with_position( - &format!("Missing element in <{}> element", elem.name()), - elem.position(), - )) + Err(parser.error_with_position(&format!( + "Missing element in <{}> element", + from_utf8!(elem.name()), + ))) } } - fn read_property( + fn read_property<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result, String> { - let prop_name = elem.attr_required("name")?; - let readable = elem.attr_bool("readable", true); - let writable = elem.attr_bool("writable", false); - let construct = elem.attr_bool("construct", false); - let construct_only = elem.attr_bool("construct-only", false); - let transfer = Transfer::from_str(elem.attr("transfer-ownership").unwrap_or("none")) - .map_err(|why| parser.fail_with_position(&why, elem.position()))?; + let prop_name = attr_or_err!(elem, b"name")?; + let readable = attr_bool!(elem, b"readable", true); + let writable = attr_bool!(elem, b"writable", false); + let construct = attr_bool!(elem, b"construct", false); + let construct_only = attr_bool!(elem, b"construct-only", false); + let transfer = attr!(elem, b"transfer-ownership"); + let transfer = Transfer::from_str(transfer.map(|a| attr_value!(a)).unwrap_or("none")) + .map_err(|why| parser.error_with_position(&why))?; let version = self.read_version(parser, ns_id, elem)?; let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?; @@ -1223,30 +1410,34 @@ impl Library { let mut doc = None; let mut doc_deprecated = None; - parser.elements(|parser, elem| match elem.name() { - "type" | "array" => { - if typ.is_some() { - return Err(parser.fail_with_position( - "Too many elements in element", - elem.position(), - )); - } - if !elem.has_attrs() && elem.name() == "type" { - // defend from - has_empty_type_tag = true; - return parser.ignore_element(); - } - typ = Some(self.read_type(parser, ns_id, elem)?); - if let Some((tid, None, _)) = typ { - typ = Some((tid, Some(EMPTY_CTYPE.to_owned()), None)); + elements!(parser, buf, elem, { + match elem.name() { + b"type" | b"array" => { + if typ.is_some() { + return Err(parser.error_with_position( + "Too many elements in element", + )); + } + if elem.attributes().count() == 0 && elem.name() == b"type" { + // defend from + has_empty_type_tag = true; + parser.ignore_element(buf) + } else { + typ = Some(self.read_type(parser, buf, ns_id, &elem)?); + if let Some((tid, None, _)) = typ { + typ = Some((tid, Some(EMPTY_CTYPE.to_owned()), None)); + } + Ok(()) + } } - Ok(()) + b"doc" => parser.text(buf, b"doc").map(|t| doc = Some(t)), + b"doc-deprecated" => parser + .text(buf, b"doc-deprecated") + .map(|t| doc_deprecated = Some(t)), + b"attribute" => parser.ignore_element(buf), + _ => return Err(parser.unexpected_element(&elem)), } - "doc" => parser.text().map(|t| doc = Some(t)), - "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)), - "attribute" => parser.ignore_element(), - _ => Err(parser.unexpected_element(elem)), - })?; + }); if has_empty_type_tag { return Ok(None); @@ -1254,7 +1445,7 @@ impl Library { if let Some((tid, c_type, _array_length)) = typ { Ok(Some(Property { - name: prop_name.into(), + name: attr_value!(prop_name).into(), readable, writable, construct, @@ -1268,48 +1459,43 @@ impl Library { doc_deprecated, })) } else { - Err(parser.fail_with_position( - "Missing element in element", - elem.position(), - )) + Err(parser.error_with_position("Missing element in element")) } } - fn read_type( + fn read_type<'a>( &mut self, - parser: &mut XmlParser<'_>, + parser: &mut XmlParser, + buf: &'a mut Vec, + ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result<(TypeId, Option, Option), String> { - let type_name = elem - .attr("name") + let type_name = attr!(elem, b"name"); + let type_name = type_name.map(|a| attr_value!(a)) .or_else(|| { - if elem.name() == "array" { + if elem.name() == b"array" { Some("array") } else { None } }) .ok_or_else(|| { - parser.fail_with_position( - " element is missing a name attribute", - elem.position(), - ) + parser.error_with_position(" element is missing a name attribute") })?; - let c_type = elem.attr("type").map(|s| s.into()); - let array_length = elem.attr("length").and_then(|s| s.parse().ok()); + let c_type = attr!(elem, b"type").map(|s| attr_value!(s).into()); + let array_length = attr!(elem, b"length").and_then(|s| attr_value!(s).parse().ok()); - let inner = parser.elements(|parser, elem| match elem.name() { - "type" | "array" => self.read_type(parser, ns_id, elem), - _ => Err(parser.unexpected_element(elem)), - })?; + let inner = elements_vec!(parser, buf, elem, { + match elem.name() { + b"type" | b"array" => self.read_type(parser, buf, ns_id, &elem), + _ => return Err(parser.unexpected_element(&elem)), + } + }); if inner.is_empty() || type_name == "GLib.ByteArray" { if type_name == "array" { - Err(parser.fail_with_position( - " element is missing an inner element type", - elem.position(), - )) + Err(parser.error_with_position(" element is missing an inner element type")) } else if type_name == "gboolean" && c_type.as_deref() == Some("_Bool") { Ok((self.find_or_stub_type(ns_id, "bool"), c_type, array_length)) } else { @@ -1325,14 +1511,13 @@ impl Library { Type::c_array( self, inner_type.0, - elem.attr("fixed-size").and_then(|n| n.parse().ok()), + attr!(elem, b"fixed-size").and_then(|n| attr_value!(n).parse().ok()), inner_type.1.clone(), ) } else { let inner = inner.iter().map(|r| r.0).collect(); - Type::container(self, type_name, inner).ok_or_else(|| { - parser.fail_with_position("Unknown container type", elem.position()) - })? + Type::container(self, type_name, inner) + .ok_or_else(|| parser.error_with_position("Unknown container type"))? }; Ok((tid, c_type, array_length)) } @@ -1340,56 +1525,43 @@ impl Library { fn read_version( &mut self, - parser: &XmlParser<'_>, + parser: &XmlParser, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result, String> { - self.read_version_attribute(parser, ns_id, elem, "version") + self.read_version_attribute(parser, ns_id, elem, b"version") } fn read_deprecated_version( &mut self, - parser: &XmlParser<'_>, + parser: &XmlParser, ns_id: u16, - elem: &Element, + elem: &BytesStart<'_>, ) -> Result, String> { - self.read_version_attribute(parser, ns_id, elem, "deprecated-version") + self.read_version_attribute(parser, ns_id, elem, b"deprecated-version") } fn read_version_attribute( &mut self, - parser: &XmlParser<'_>, + parser: &XmlParser, ns_id: u16, - elem: &Element, - attr: &str, + elem: &BytesStart<'_>, + attr: &[u8], ) -> Result, String> { - if let Some(v) = elem.attr(attr) { - match v.parse() { + if let Some(v) = attr!(elem, attr) { + match attr_value!(v).parse() { Ok(v) => { self.register_version(ns_id, v); Ok(Some(v)) } - Err(e) => Err(parser.fail(&format!("Invalid `{}` attribute: {}", attr, e))), + Err(e) => { + Err(parser.error(&format!("Invalid `{}` attribute: {}", from_utf8!(attr), e))) + } } } else { Ok(None) } } - - fn read_object_c_type<'a>( - &mut self, - parser: &mut XmlParser<'_>, - elem: &'a Element, - ) -> Result<&'a str, String> { - elem.attr("type") - .or_else(|| elem.attr("type-name")) - .ok_or_else(|| { - parser.fail(&format!( - "Missing `c:type`/`glib:type-name` attributes on element <{}>", - elem.name() - )) - }) - } } fn make_file_name(dir: &Path, name: &str) -> PathBuf { diff --git a/src/xmlparser.rs b/src/xmlparser.rs index e860769dc..9872e3818 100644 --- a/src/xmlparser.rs +++ b/src/xmlparser.rs @@ -1,464 +1,187 @@ -use std::{ - fmt, - fs::File, - io::{BufReader, Read}, - path::{Path, PathBuf}, - rc::Rc, - str, -}; -use xml::{ - self, - attribute::OwnedAttribute, - common::{Position, TextPosition}, - name::OwnedName, - reader::{EventReader, XmlEvent}, -}; - -/// NOTE: After parser returns an error its further behaviour is unspecified. -pub struct XmlParser<'a> { - /// Inner XML parser doing actual work. - parser: EventReader>, - /// Next event to be returned. - /// - /// Takes priority over events returned from inner parser. - /// Used to support peaking one element ahead. - peek_event: Option>, - /// Position on peek event if any. - peek_position: TextPosition, - /// Used to emits errors. Rc so that it can be cheaply shared with Element type. - error_emitter: Rc, +// use std::{ +// fmt, +// fs::File, +// io::{BufReader, Read}, +// path::{Path, PathBuf}, +// rc::Rc, +// str, +// }; +// use xml::{ +// self, +// attribute::OwnedAttribute, +// common::{Position, TextPosition}, +// name::OwnedName, +// reader::{EventReader, XmlEvent}, +// }; + +use quick_xml::events::{BytesStart, Event}; +use quick_xml::Reader; + +use std::fmt; +use std::fs::File; +use std::io::BufReader; +use std::path::PathBuf; +use std::str::FromStr; + +pub struct XmlParser { + reader: Reader>, + file_path: PathBuf, } -struct ErrorEmitter { - /// Path to currently parsed document. - path: Option, -} +impl XmlParser { + pub fn new(file_path: PathBuf) -> Result { + let f = File::open(&file_path) + .map_err(|e| format!("Failed to open `{}`: {}", file_path.display(), e))?; + let f = BufReader::new(f); -impl ErrorEmitter { - pub fn emit(&self, message: &str, position: TextPosition) -> String { - let enriched = match self.path { - Some(ref path) => format!("{} at line {}: {}", path.display(), position, message), - None => format!("{} {}", position, message), - }; - format!("GirXml: {}", enriched) - } - - pub fn emit_error(&self, error: &xml::reader::Error) -> String { - // Error returned by EventReader already includes the position. - // That is why we have a separate implementation that only - // prepends the file path. - let enriched = match self.path { - Some(ref path) => format!("{}:{}", path.display(), error), - None => format!("{}", error), - }; - format!("GirXml: {}", enriched) - } -} - -/// A wrapper for `XmlEvent::StartDocument` which doesn't have its own type. -pub struct Document; - -/// A wrapper for `XmlEvent::StartElement` which doesn't have its own type. -pub struct Element { - name: OwnedName, - attributes: Vec, - position: TextPosition, - error_emitter: Rc, -} - -impl Element { - /// Returns the element local name. - pub fn name(&self) -> &str { - &self.name.local_name - } - - /// Value of attribute with given name or None if it is not found. - pub fn attr(&self, name: &str) -> Option<&str> { - for attr in &self.attributes { - if attr.name.local_name == name { - return Some(&attr.value); - } - } - None - } - - /// Checks if elements has any attributes. - pub fn has_attrs(&self) -> bool { - !self.attributes.is_empty() - } - - pub fn attr_bool(&self, name: &str, default: bool) -> bool { - for attr in &self.attributes { - if attr.name.local_name == name { - return attr.value == "1"; - } - } - default + Ok(Self { + reader: Reader::from_reader(f), + file_path, + }) } - pub fn attr_from_str(&self, name: &str) -> Result, String> - where - T: str::FromStr, - T::Err: fmt::Display, - { - if let Some(value_str) = self.attr(name) { - match T::from_str(value_str) { - Ok(value) => Ok(Some(value)), - Err(error) => { - let message = format!( - "Attribute `{}` on element <{}> has invalid value: {}", - name, - self.name(), - error - ); - Err(self.error_emitter.emit(&message, self.position)) + pub fn get_next_if_tag_is<'a, 'b: 'a>( + &mut self, + buf: &'b mut Vec, + expected_tag: &[u8], + ) -> Result, String> { + match self.next_event(buf)? { + Event::Start(e) => { + if e.name() == expected_tag { + Ok(e) + } else { + Err(format!("Unexpected element <{}>", unsafe { + std::str::from_utf8_unchecked(e.name()) + },)) } } - } else { - Ok(None) + Event::Text(e) => Err("Expected a tag, found text".to_owned()), + _ => unreachable!(), } } - /// Returns element position. - pub fn position(&self) -> TextPosition { - self.position - } - - /// Value of attribute with given name or an error when absent. - pub fn attr_required(&self, name: &str) -> Result<&str, String> { - for attr in &self.attributes { - if attr.name.local_name == name { - return Ok(&attr.value); - } - } - let message = format!( - "Attribute `{}` on element <{}> is required.", - name, - self.name() - ); - Err(self.error_emitter.emit(&message, self.position)) - } -} - -impl<'a> XmlParser<'a> { - pub fn from_path(path: &Path) -> Result, String> { - match File::open(&path) { - Err(e) => Err(format!("Can't open file \"{}\": {}", path.display(), e)), - Ok(file) => Ok(XmlParser { - parser: EventReader::new(Box::new(BufReader::new(file))), - peek_event: None, - peek_position: TextPosition::new(), - error_emitter: Rc::new(ErrorEmitter { - path: Some(path.to_owned()), - }), - }), - } - } - - #[cfg(test)] - pub fn new<'r, R: 'r + Read>(read: R) -> Result, String> { - Ok(XmlParser { - parser: EventReader::new(Box::new(read)), - peek_event: None, - peek_position: TextPosition::new(), - error_emitter: Rc::new(ErrorEmitter { path: None }), + pub fn next_event<'a, 'b: 'a>(&mut self, buf: &'b mut Vec) -> Result, String> { + self.reader.read_event(buf).map_err(|e| { + format!( + "Error at position {}: {:?}", + self.reader.buffer_position(), + e + ) }) } - /// Returns an error that combines current position and given error message. - pub fn fail(&self, message: &str) -> String { - self.error_emitter.emit(message, self.position()) - } - - /// Returns an error that combines given error message and position. - pub fn fail_with_position(&self, message: &str, position: TextPosition) -> String { - self.error_emitter.emit(message, position) - } - - pub fn unexpected_element(&self, elem: &Element) -> String { - let message = format!("Unexpected element <{}>", elem.name()); - self.error_emitter.emit(&message, elem.position()) - } - - fn unexpected_event(&self, event: &XmlEvent) -> String { - let message = format!("Unexpected event {:?}", event); - self.error_emitter.emit(&message, self.position()) - } - - pub fn position(&self) -> TextPosition { - match self.peek_event { - None => self.parser.position(), - Some(_) => self.peek_position, - } - } - - /// Returns next XML event without consuming it. - fn peek_event(&mut self) -> &Result { - if self.peek_event.is_none() { - self.peek_event = Some(self.next_event_impl()); - self.peek_position = self.parser.position(); - } - self.peek_event.as_ref().unwrap() - } - - /// Consumes and returns next XML event. - fn next_event(&mut self) -> Result { - match self.peek_event.take() { - None => self.next_event_impl(), - Some(e) => e, - } - } - - /// Returns next XML event directly from parser. - fn next_event_impl(&mut self) -> Result { + pub fn next_element<'a, 'b: 'a>(&mut self, buf: &'b mut Vec) -> Result, String> { loop { - match self.parser.next() { - // Ignore whitespace and comments by default. - Ok(XmlEvent::Whitespace(..) | XmlEvent::Comment(..)) => continue, - Ok(event) => return Ok(event), - Err(e) => return Err(self.error_emitter.emit_error(&e)), - } + return match self.next_event(buf) { + Ok(Event::Start(e)) => Ok(e), + Ok(Event::End(_)) => Err("Unexpected Event::End".to_owned()), + Ok(Event::Eof) => Err("Reached end of file".to_owned()), + Err(e) => Err(format!( + "Error at position {}: {:?}", + self.reader.buffer_position(), + e + )), + _ => continue, + }; } } - pub fn document(&mut self, f: F) -> Result - where - F: FnOnce(&mut XmlParser<'_>, Document) -> Result, - { - let doc = self.start_document()?; - let result = f(self, doc)?; - self.end_document()?; - Ok(result) - } - - fn start_document(&mut self) -> Result { - match self.next_event()? { - XmlEvent::StartDocument { .. } => Ok(Document), - e => Err(self.unexpected_event(&e)), - } - } - - fn end_document(&mut self) -> Result<(), String> { - match self.next_event()? { - XmlEvent::EndDocument { .. } => Ok(()), - e => Err(self.unexpected_event(&e)), - } - } - - pub fn elements(&mut self, mut f: F) -> Result, String> - where - F: FnMut(&mut XmlParser<'_>, &Element) -> Result, - { - let mut results = Vec::new(); + pub fn end_element<'a>(&mut self, buf: &'a mut Vec) -> Result<(), String> { loop { - match *self.peek_event() { - Ok(XmlEvent::StartElement { .. }) => { - let element = self.start_element()?; - results.push(f(self, &element)?); - self.end_element()?; - } - _ => return Ok(results), - } - } - } - - pub fn element_with_name(&mut self, expected_name: &str, f: F) -> Result - where - F: FnOnce(&mut XmlParser<'_>, &Element) -> Result, - { - let elem = self.start_element()?; - if expected_name != elem.name.local_name { - return Err(self.unexpected_element(&elem)); + return match self.next_event(buf) { + Ok(Event::End(e)) => Ok(()), + Ok(Event::Start(_)) => Err("Unexpected Event::Start".to_owned()), + Ok(Event::Eof) => Err("Reached end of file".to_owned()), + Err(e) => Err(format!( + "Error at position {}: {:?}", + self.reader.buffer_position(), + e + )), + _ => continue, + }; } - let result = f(self, &elem)?; - self.end_element()?; - Ok(result) } - fn start_element(&mut self) -> Result { - match self.next_event() { - Ok(XmlEvent::StartElement { - name, attributes, .. - }) => Ok(Element { - name, - attributes, - position: self.position(), - error_emitter: self.error_emitter.clone(), - }), - Ok(e) => Err(self.unexpected_event(&e)), - Err(e) => Err(e), - } + pub fn unexpected_element(&self, elem: &BytesStart<'_>) -> String { + self.error_with_position(&format!( + "Unexpected element <{}>", + std::str::from_utf8(elem.name()).unwrap() + )) } - fn end_element(&mut self) -> Result<(), String> { - match self.next_event() { - Ok(XmlEvent::EndElement { .. }) => Ok(()), - Ok(e) => Err(self.unexpected_event(&e)), - Err(e) => Err(e), - } + pub fn error(&self, msg: &str) -> String { + format!("GirXml {}: {}", self.file_path.display(), msg) } - pub fn text(&mut self) -> Result { - let mut result = String::new(); - loop { - match *self.peek_event() { - Ok(XmlEvent::Characters(..)) => { - if let Ok(XmlEvent::Characters(s)) = self.next_event() { - result.push_str(&s); - } - } - Err(_) => { - self.next_event()?; - unreachable!(); - } - _ if result.is_empty() => { - return Err(self.fail("Expected text content")); - } - _ => break, - } - } - Ok(result) + pub fn error_with_position(&self, msg: &str) -> String { + format!( + "GirXml {} at position {}: {}", + self.file_path.display(), + self.reader.buffer_position(), + msg + ) } /// Ignore everything within current element. - pub fn ignore_element(&mut self) -> Result<(), String> { + pub fn ignore_element<'a>(&mut self, buf: &'a mut Vec) -> Result<(), String> { let mut depth = 1; loop { - match *self.peek_event() { - Ok(XmlEvent::StartElement { .. }) => { + match self.next_event(buf) { + Ok(Event::Start(_)) => { // Ignore warning about unused result, we know event is OK. - drop(self.next_event()); depth += 1; } - Ok(XmlEvent::EndElement { .. }) => { + Ok(Event::End(_)) => { depth -= 1; - if depth > 0 { - drop(self.next_event()); - } else { + if depth < 1 { return Ok(()); } } - Ok(_) => drop(self.next_event()), - Err(_) => return self.next_event().map(|_| ()), + Ok(_) => {} + Err(e) => return Err(e), } } } -} -#[cfg(test)] -mod tests { - - use super::*; + pub fn text(&mut self, buf: &mut Vec, elem_name: &[u8]) -> Result { + self.reader.read_text(elem_name, buf).map_err(|e| { + format!( + "Error at position {}: {:?}", + self.reader.buffer_position(), + e + ) + }) + } - fn with_parser(xml: &[u8], f: F) -> Result + pub fn attr_from_str( + &self, + elem: &BytesStart<'_>, + attr_name: &[u8], + ) -> Result, String> where - F: FnOnce(XmlParser<'_>) -> Result, + T: FromStr, + T::Err: fmt::Display, { - f(XmlParser::new(xml)?) - } - - #[test] - fn test_element_with_name() { - let xml = br#" - - - "#; - - fn parse_with_root_name(xml: &[u8], root: &str) -> Result<(), String> { - with_parser(xml, |mut p| { - p.document(|p, _| p.element_with_name(root, |_, _elem| Ok(()))) - }) + if let Some(attr) = elem + .attributes() + .filter_map(|n| n.ok()) + .find(|n| n.key == attr_name) + { + let value_str = std::str::from_utf8(&attr.value).unwrap(); + match T::from_str(value_str) { + Ok(value) => Ok(Some(value)), + Err(error) => { + let message = format!( + "Attribute `{}` on element <{}> has invalid value: {}", + std::str::from_utf8(attr_name).unwrap(), + std::str::from_utf8(elem.name()).unwrap(), + error, + ); + Err(self.error_with_position(&message)) + } + } + } else { + Ok(None) } - - assert!(parse_with_root_name(xml, "a").is_ok()); - assert!(parse_with_root_name(xml, "b").is_err()); - } - - #[test] - fn test_ignore_element() { - let xml = br#" - - - - - - some text content - "#; - - with_parser(xml, |mut p| { - p.document(|p, _| p.element_with_name("a", |p, _| p.ignore_element())) - }) - .unwrap(); - } - - #[test] - fn test_elements() { - let xml = br#" - - - - - "#; - - let result: String = with_parser(xml, |mut p| { - p.document(|p, _| { - p.element_with_name("root", |p, _| { - p.elements(|_, elem| elem.attr_required("name").map(|s| s.to_owned())) - .map(|v| v.join(".")) - }) - }) - }) - .unwrap(); - - assert_eq!("a.b.c", result); - } - - #[test] - fn test_text() { - let xml = br#" - hello world!"#; - - let result: String = with_parser(xml, |mut p| { - p.document(|p, _| p.element_with_name("x", |p, _| p.text())) - }) - .unwrap(); - - assert_eq!("hello world!", &result); - } - - #[test] - fn test_attr_required() { - let xml = br#" - "#; - - with_parser(xml, |mut p| { - p.document(|p, _| { - p.element_with_name("x", |_, elem| { - assert!(elem.attr_required("a").is_ok()); - assert!(elem.attr_required("b").is_ok()); - assert!(elem.attr_required("c").is_err()); - assert!(elem.attr_required("d").is_err()); - Ok(()) - }) - }) - }) - .unwrap(); - } - - #[test] - fn test_attr_from_str() { - let xml = br#" - "#; - - with_parser(xml, |mut p| { - p.document(|p, _| { - p.element_with_name("x", |_, elem| { - assert_eq!(elem.attr_from_str::("a").unwrap(), Some(123)); - assert!(elem.attr_from_str::("b").is_err()); - Ok(()) - }) - }) - }) - .unwrap(); } }