use quick_xml::errors::IllFormedError;
use quick_xml::escape::resolve_predefined_entity;
use quick_xml::events::attributes::{Attribute, Attributes};
use quick_xml::events::{BytesStart, Event};
use quick_xml::{Error, Reader, XmlVersion};
use std::borrow::Cow;
use std::cell::RefCell;
use std::io::BufRead;
type BufferHandle = usize;
struct BufferManager {
buffers: Vec<Vec<u8>>,
free_buffers: Vec<BufferHandle>,
}
impl BufferManager {
fn alloc(&mut self) -> (&mut Vec<u8>, BufferHandle) {
if let Some(index) = self.free_buffers.pop() {
let mut_buffer = self.buffers.get_mut(index).unwrap();
mut_buffer.clear();
(mut_buffer, index)
} else {
let index = self.buffers.len();
self.buffers.push(Vec::new());
let mut_buffer = self.buffers.last_mut().unwrap();
(mut_buffer, index)
}
}
fn release(&mut self, index: BufferHandle) {
self.free_buffers.push(index);
}
}
pub struct PullReader<B: BufRead> {
reader: Reader<B>,
buffer_manager: RefCell<BufferManager>,
}
pub struct Element<'a, B: BufRead> {
pending_close: bool,
handle: BufferHandle,
reader: &'a mut Reader<B>,
buffer_manager: &'a RefCell<BufferManager>,
data: BytesStart<'a>,
}
pub struct AttributesView<'a> {
attributes: Attributes<'a>,
}
pub struct AttributeView<'a> {
attribute: Attribute<'a>,
}
pub struct Text<'a> {
data: Cow<'a, str>,
}
impl<B: BufRead> PullReader<B> {
pub fn new(input: B) -> Self {
let mut reader = Reader::from_reader(input);
let config = reader.config_mut();
config.expand_empty_elements = true;
config.check_end_names = true;
PullReader::from_reader(reader)
}
pub fn from_reader(reader: Reader<B>) -> Self {
PullReader {
reader,
buffer_manager: RefCell::new(BufferManager {
buffers: Vec::new(),
free_buffers: Vec::new(),
}),
}
}
pub fn root<'a>(&'a mut self) -> quick_xml::Result<Element<'a, B>> {
let mut buffer_manager = self.buffer_manager.borrow_mut();
let (buffer, handle) = buffer_manager.alloc();
loop {
match self.reader.read_event_into(buffer)? {
Event::Start(data) => {
return Ok(Element {
pending_close: false,
handle,
reader: &mut self.reader,
buffer_manager: &self.buffer_manager,
data: unsafe {
std::mem::transmute::<BytesStart<'_>, BytesStart<'a>>(data)
},
});
}
Event::Eof => {
buffer_manager.release(handle);
return Err(Error::IllFormed(IllFormedError::MissingEndTag(
"root".to_owned(),
)));
}
_ => {}
}
}
}
}
impl<B: BufRead> Element<'_, B> {
pub fn name(&'_ self) -> Cow<'_, str> {
Cow::Borrowed(self.data.name().into_inner())
}
pub fn attributes(&'_ self) -> AttributesView<'_> {
AttributesView {
attributes: self.data.attributes(),
}
}
pub fn find_attribute(
&'_ self,
name: impl AsRef<str>,
) -> quick_xml::Result<Option<AttributeView<'_>>> {
for attribute in self.attributes() {
match attribute {
Ok(attribute) => {
if attribute.key().as_ref() == name.as_ref() {
return Ok(Some(attribute));
}
}
Err(error) => return Err(error),
}
}
Ok(None)
}
pub fn next_child<'a>(&'a mut self) -> quick_xml::Result<Option<Element<'a, B>>> {
if self.pending_close {
return Ok(None);
}
let mut buffer_manager = self.buffer_manager.borrow_mut();
let (buffer, handle) = buffer_manager.alloc();
loop {
buffer.clear();
match self.reader.read_event_into(buffer)? {
Event::Start(data) => {
return Ok(Some(Element {
pending_close: false,
handle,
buffer_manager: self.buffer_manager,
reader: self.reader,
data: unsafe {
std::mem::transmute::<BytesStart<'_>, BytesStart<'a>>(data)
},
}));
}
Event::End(_) => {
buffer_manager.release(handle);
self.pending_close = true;
return Ok(None);
}
Event::Eof => {
buffer_manager.release(handle);
return Err(Error::IllFormed(IllFormedError::MissingEndTag(
"next_child".to_owned(),
)));
}
_ => {}
}
}
}
pub fn find_next_child<'a>(
&'a mut self,
name: impl AsRef<str>,
) -> quick_xml::Result<Option<Element<'a, B>>> {
while let Some(element) = self.next_child()? {
if element.data.name().into_inner() == name.as_ref() {
return Ok(Some(unsafe {
std::mem::transmute::<Element<'_, B>, Element<'a, B>>(element)
}));
}
}
Ok(None)
}
pub fn try_text<'a>(&'a mut self) -> quick_xml::Result<Option<Text<'a>>> {
if self.pending_close {
return Ok(None);
}
let mut buffer_manager = self.buffer_manager.borrow_mut();
let (buffer, handle) = buffer_manager.alloc();
let result = read_text(self.reader, buffer);
buffer_manager.release(handle);
let text = result?;
self.pending_close = true;
let trimmed = text.trim();
if trimmed.is_empty() {
Ok(None)
} else if trimmed.len() == text.len() {
Ok(Some(Text {
data: Cow::Owned(text),
}))
} else {
Ok(Some(Text {
data: Cow::Owned(trimmed.to_owned()),
}))
}
}
pub fn text(&'_ mut self) -> quick_xml::Result<Text<'_>> {
match self.try_text() {
Ok(Some(text)) => Ok(text),
Ok(None) => Ok(Text {
data: Cow::Owned(String::new()),
}),
Err(error) => Err(error),
}
}
}
impl<B: BufRead> Drop for Element<'_, B> {
fn drop(&mut self) {
let mut buffer_manager = self.buffer_manager.borrow_mut();
buffer_manager.release(self.handle);
if !self.pending_close {
let mut depth = 1;
let (tmp_buffer, tmp_handle) = buffer_manager.alloc();
loop {
if let Ok(event) = self.reader.read_event_into(tmp_buffer) {
match event {
Event::Start(_) => depth += 1,
Event::End(_) => {
depth -= 1;
if depth == 0 {
buffer_manager.release(tmp_handle);
return;
}
}
Event::Eof => {
buffer_manager.release(tmp_handle);
return;
}
Event::Text(_) => {}
Event::CData(_) => {}
_ => {}
}
tmp_buffer.clear();
} else {
buffer_manager.release(tmp_handle);
return;
}
}
}
}
}
impl AttributeView<'_> {
pub fn key(&'_ self) -> Cow<'_, str> {
Cow::Borrowed(self.attribute.key.into_inner())
}
pub fn value(&'_ self) -> quick_xml::Result<Text<'_>> {
Ok(Text {
data: self.attribute.normalized_value(XmlVersion::Implicit1_0)?,
})
}
}
impl<'a> Iterator for AttributesView<'a> {
type Item = quick_xml::Result<AttributeView<'a>>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(attribute_result) = self.attributes.next() {
match attribute_result {
Ok(attribute) => Some(Ok(AttributeView { attribute })),
Err(error) => Some(Err(Error::InvalidAttr(error))),
}
} else {
None
}
}
}
impl Text<'_> {
pub fn contents(&'_ self) -> Cow<'_, str> {
Cow::Borrowed(self.data.as_ref())
}
}
fn read_text<B: BufRead>(
reader: &mut Reader<B>,
buffer: &mut Vec<u8>,
) -> quick_xml::Result<String> {
let mut text = String::new();
let mut depth = 1;
loop {
buffer.clear();
match reader.read_event_into(buffer)? {
Event::Start(_) => depth += 1,
Event::End(_) => {
depth -= 1;
if depth == 0 {
return Ok(text);
}
}
Event::Text(data) => text.push_str(&data),
Event::CData(data) => text.push_str(&data),
Event::GeneralRef(entity) => match entity.resolve_char_ref()? {
Some(ch) => text.push(ch),
None => match resolve_predefined_entity(&entity) {
Some(value) => text.push_str(value),
None => {
text.push('&');
text.push_str(&entity);
text.push(';');
}
},
},
Event::Eof => {
return Err(Error::IllFormed(IllFormedError::MissingEndTag(
"try_text".to_owned(),
)));
}
_ => {}
}
}
}
#[cfg(test)]
mod tests {
use super::PullReader;
fn text_of(xml: &[u8]) -> quick_xml::Result<String> {
let mut reader = PullReader::new(xml);
let mut root = reader.root()?;
let mut child = root.next_child()?.unwrap();
Ok(child.text()?.contents().into_owned())
}
#[test]
fn text_is_read_and_trimmed() {
assert_eq!(
text_of(b"<x><a>\n foo bar \n</a></x>").unwrap(),
"foo bar"
);
assert_eq!(
text_of(b"<x><a>Gr\xc3\xb6\xc3\x9fe</a></x>").unwrap(),
"Größe"
);
assert_eq!(text_of(b"<x><a> </a></x>").unwrap(), "");
assert_eq!(text_of(b"<x><a/></x>").unwrap(), "");
}
#[test]
fn text_with_entities_is_fully_read() {
assert_eq!(text_of(b"<x><a>A & B</a></x>").unwrap(), "A & B");
assert_eq!(
text_of(b"<x><a><b> "'</a></x>").unwrap(),
"<b> \"'"
);
assert_eq!(text_of(b"<x><a>10A</a></x>").unwrap(), "10A");
assert_eq!(text_of(b"<x><a>&foo; bar</a></x>").unwrap(), "&foo; bar");
}
#[test]
fn text_with_cdata_is_fully_read() {
assert_eq!(text_of(b"<x><a><![CDATA[<b>]]></a></x>").unwrap(), "<b>");
assert_eq!(
text_of(b"<x><a>foo <![CDATA[bar]]> baz</a></x>").unwrap(),
"foo bar baz"
);
}
#[test]
fn text_includes_nested_elements() {
assert_eq!(text_of(b"<x><a>foo <b>bar</b></a></x>").unwrap(), "foo bar");
}
#[test]
fn attribute_values_are_unescaped() {
let mut reader = PullReader::new(r#"<x a="1 & 2 <3"/>"#.as_bytes());
let root = reader.root().unwrap();
let attribute = root.find_attribute("a").unwrap().unwrap();
assert_eq!(attribute.value().unwrap().contents(), "1 & 2 <3");
assert!(root.find_attribute("b").unwrap().is_none());
}
#[test]
fn dropped_elements_are_skipped() {
let mut reader = PullReader::new("<x><a><b>1</b><b>2</b></a><c>3</c></x>".as_bytes());
let mut root = reader.root().unwrap();
drop(root.next_child().unwrap());
let mut c = root.next_child().unwrap().unwrap();
assert_eq!(c.name(), "c");
assert_eq!(c.text().unwrap().contents(), "3");
drop(c);
assert!(root.next_child().unwrap().is_none());
}
#[test]
fn text_is_read_after_skipping_siblings() {
let data = "<x><a>1</a><b>2</b><a>3</a></x>";
let mut reader = PullReader::new(data.as_bytes());
let mut root = reader.root().unwrap();
assert_eq!(
root.find_next_child("b")
.unwrap()
.unwrap()
.text()
.unwrap()
.contents(),
"2"
);
assert_eq!(
root.find_next_child("a")
.unwrap()
.unwrap()
.text()
.unwrap()
.contents(),
"3"
);
assert!(root.find_next_child("a").unwrap().is_none());
}
#[test]
fn ill_formed_xml_is_reported() {
assert!(text_of(b"<x><a>foo</b></x>").is_err());
assert!(text_of(b"<x><a>foo").is_err());
assert!(PullReader::new("".as_bytes()).root().is_err());
}
#[test]
fn non_utf8_input_is_rejected() {
let latin1 = b"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><x><a>Gr\xf6\xdfe</a></x>";
assert!(text_of(latin1).is_err());
}
}