Skip to content

Commit

Permalink
Introduce an AsyncReader instead of overloading the Reader
Browse files Browse the repository at this point in the history
  • Loading branch information
endor committed Sep 27, 2020
1 parent 5080421 commit fb763a5
Show file tree
Hide file tree
Showing 15 changed files with 2,718 additions and 783 deletions.
91 changes: 71 additions & 20 deletions examples/issue68.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#![allow(unused)]

use quick_xml::events::Event;
#[cfg(feature = "asynchronous")]
use quick_xml::AsyncReader;
use quick_xml::Reader;
use std::io::Read;
#[cfg(feature = "asynchronous")]
Expand Down Expand Up @@ -55,44 +57,87 @@ impl Response {
}
}

fn parse_report(xml_data: &str) -> Vec<Resource> {
#[derive(Clone, Copy)]
enum State {
Root,
MultiStatus,
Response,
Success,
Error,
}

#[cfg(feature = "asynchronous")]
async fn parse_report_async(xml_data: &str) -> Vec<Resource> {
let result = Vec::<Resource>::new();

let mut reader = Reader::from_str(xml_data);
let mut reader = AsyncReader::from_str(xml_data);
reader.trim_text(true);

let mut count = 0;
let mut buf = Vec::new();
let mut ns_buffer = Vec::new();

#[derive(Clone, Copy)]
enum State {
Root,
MultiStatus,
Response,
Success,
Error,
};

let mut responses = Vec::<Response>::new();
let mut current_response = Response::new();
let mut current_prop = Prop::new();

let mut depth = 0;
let mut state = State::MultiStatus;

#[cfg(feature = "asynchronous")]
let mut runtime = Runtime::new().expect("Runtime cannot be initialized");

loop {
#[cfg(feature = "asynchronous")]
let event = runtime
.block_on(async { reader.read_namespaced_event(&mut buf, &mut ns_buffer).await });
match reader.read_namespaced_event(&mut buf, &mut ns_buffer).await {
Ok((namespace_value, Event::Start(e))) => {
let namespace_value = namespace_value.unwrap_or_default();
match (depth, state, namespace_value, e.local_name()) {
(0, State::Root, b"DAV:", b"multistatus") => state = State::MultiStatus,
(1, State::MultiStatus, b"DAV:", b"response") => {
state = State::Response;
current_response = Response::new();
}
(2, State::Response, b"DAV:", b"href") => {
current_response.href = e.unescape_and_decode(&reader).unwrap();
}
_ => {}
}
depth += 1;
}
Ok((namespace_value, Event::End(e))) => {
let namespace_value = namespace_value.unwrap_or_default();
let local_name = e.local_name();
match (depth, state, &*namespace_value, local_name) {
(1, State::MultiStatus, b"DAV:", b"multistatus") => state = State::Root,
(2, State::MultiStatus, b"DAV:", b"multistatus") => state = State::MultiStatus,
_ => {}
}
depth -= 1;
}
Ok((_, Event::Eof)) => break,
Err(e) => break,
_ => (),
}
}
result
}

fn parse_report(xml_data: &str) -> Vec<Resource> {
let result = Vec::<Resource>::new();

let mut reader = Reader::from_str(xml_data);
reader.trim_text(true);

#[cfg(not(feature = "asynchronous"))]
let event = reader.read_namespaced_event(&mut buf, &mut ns_buffer);
let mut count = 0;
let mut buf = Vec::new();
let mut ns_buffer = Vec::new();

let mut responses = Vec::<Response>::new();
let mut current_response = Response::new();
let mut current_prop = Prop::new();

let mut depth = 0;
let mut state = State::MultiStatus;

match event {
loop {
match reader.read_namespaced_event(&mut buf, &mut ns_buffer) {
Ok((namespace_value, Event::Start(e))) => {
let namespace_value = namespace_value.unwrap_or_default();
match (depth, state, namespace_value, e.local_name()) {
Expand Down Expand Up @@ -148,4 +193,10 @@ fn main() {
"#;

parse_report(test_data);

#[cfg(feature = "asynchronous")]
let mut runtime = Runtime::new().expect("Runtime cannot be initialized");

#[cfg(feature = "asynchronous")]
runtime.block_on(async { parse_report_async(test_data).await });
}
111 changes: 86 additions & 25 deletions examples/nested_readers.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use quick_xml::events::Event;
#[cfg(feature = "asynchronous")]
use quick_xml::AsyncReader;
use quick_xml::Reader;
#[cfg(feature = "asynchronous")]
use tokio::runtime::Runtime;
Expand All @@ -10,34 +12,18 @@ struct TableStat {
index: u8,
rows: Vec<Vec<String>>,
}
// demonstrate how to nest readers
// This is useful for when you need to traverse
// a few levels of a document to extract things.
fn main() -> Result<(), quick_xml::Error> {

fn nest_readers() -> Result<(), quick_xml::Error> {
let mut buf = Vec::new();
// buffer for nested reader
let mut skip_buf = Vec::new();
let mut count = 0;

#[cfg(feature = "asynchronous")]
let mut runtime = Runtime::new().expect("Runtime cannot be initialized");

#[cfg(feature = "asynchronous")]
let mut reader =
runtime.block_on(async { Reader::from_file("tests/documents/document.xml").await })?;

#[cfg(not(feature = "asynchronous"))]
let mut reader = Reader::from_file("tests/documents/document.xml")?;

let mut found_tables = Vec::new();
loop {
#[cfg(feature = "asynchronous")]
let event = runtime.block_on(async { reader.read_event(&mut buf).await })?;

#[cfg(not(feature = "asynchronous"))]
let event = reader.read_event(&mut buf)?;

match event {
match reader.read_event(&mut buf)? {
Event::Start(element) => match element.name() {
b"w:tbl" => {
count += 1;
Expand All @@ -51,14 +37,74 @@ fn main() -> Result<(), quick_xml::Error> {
loop {
skip_buf.clear();

#[cfg(feature = "asynchronous")]
let event =
runtime.block_on(async { reader.read_event(&mut skip_buf).await })?;
match reader.read_event(&mut skip_buf)? {
Event::Start(element) => match element.name() {
b"w:tr" => {
stats.rows.push(vec![]);
row_index = stats.rows.len() - 1;
}
b"w:tc" => {
stats.rows[row_index]
.push(String::from_utf8(element.name().to_vec()).unwrap());
}
_ => {}
},
Event::End(element) => {
if element.name() == b"w:tbl" {
found_tables.push(stats);
break;
}
}
_ => {}
}
}
}
_ => {}
},
Event::Eof => break,
_ => {}
}
buf.clear();
}
assert_eq!(found_tables.len(), 2);
// pretty print the table
println!("{:#?}", found_tables);
assert_eq!(found_tables[0].rows.len(), 2);
assert_eq!(found_tables[0].rows[0].len(), 4);
assert_eq!(found_tables[0].rows[1].len(), 4);

assert_eq!(found_tables[1].rows.len(), 2);
assert_eq!(found_tables[1].rows[0].len(), 4);
assert_eq!(found_tables[1].rows[1].len(), 4);
Ok(())
}

#[cfg(feature = "asynchronous")]
async fn nest_readers_async() -> Result<(), quick_xml::Error> {
let mut buf = Vec::new();
// buffer for nested reader
let mut skip_buf = Vec::new();
let mut count = 0;

#[cfg(not(feature = "asynchronous"))]
let event = reader.read_event(&mut skip_buf)?;
let mut reader = AsyncReader::from_file("tests/documents/document.xml").await?;

match event {
let mut found_tables = Vec::new();
loop {
match reader.read_event(&mut buf).await? {
Event::Start(element) => match element.name() {
b"w:tbl" => {
count += 1;
let mut stats = TableStat {
index: count,
rows: vec![],
};
// must define stateful variables
// outside the nested loop else they are overwritten
let mut row_index = 0;
loop {
skip_buf.clear();

match reader.read_event(&mut skip_buf).await? {
Event::Start(element) => match element.name() {
b"w:tr" => {
stats.rows.push(vec![]);
Expand Down Expand Up @@ -99,3 +145,18 @@ fn main() -> Result<(), quick_xml::Error> {
assert_eq!(found_tables[1].rows[1].len(), 4);
Ok(())
}

// demonstrate how to nest readers
// This is useful for when you need to traverse
// a few levels of a document to extract things.
fn main() -> Result<(), quick_xml::Error> {
#[cfg(feature = "asynchronous")]
let mut runtime = Runtime::new().expect("Runtime cannot be initialized");

#[cfg(feature = "asynchronous")]
runtime.block_on(async { nest_readers_async().await })?;

nest_readers()?;

Ok(())
}
69 changes: 45 additions & 24 deletions examples/read_texts.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,48 @@
use quick_xml::events::Event;
#[cfg(feature = "asynchronous")]
use quick_xml::AsyncReader;
use quick_xml::Reader;
#[cfg(feature = "asynchronous")]
use tokio::runtime::Runtime;

fn main() {
use quick_xml::events::Event;
use quick_xml::Reader;
#[cfg(feature = "asynchronous")]
async fn read_text_async(xml: &str) {
let mut reader = AsyncReader::from_str(xml);
reader.trim_text(true);

let xml = "<tag1>text1</tag1><tag1>text2</tag1>\
<tag1>text3</tag1><tag1><tag2>text4</tag2></tag1>";
let mut txt = Vec::new();
let mut buf = Vec::new();

loop {
match reader.read_event(&mut buf).await {
Ok(Event::Start(ref e)) if e.name() == b"tag2" => {
#[cfg(feature = "asynchronous")]
let text = reader
.read_text(b"tag2", &mut Vec::new())
.await
.expect("Cannot decode text value");

txt.push(text);
println!("{:?}", txt);
}
Ok(Event::Eof) => break, // exits the loop when reaching end of file
Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
_ => (), // There are several other `Event`s we do not consider here
}
buf.clear();
}
}

fn read_text(xml: &str) {
let mut reader = Reader::from_str(xml);
reader.trim_text(true);

let mut txt = Vec::new();
let mut buf = Vec::new();

#[cfg(feature = "asynchronous")]
let mut runtime = Runtime::new().expect("Runtime cannot be initialized");

loop {
#[cfg(feature = "asynchronous")]
let event = runtime.block_on(async { reader.read_event(&mut buf).await });

#[cfg(not(feature = "asynchronous"))]
let event = reader.read_event(&mut buf);

match event {
match reader.read_event(&mut buf) {
Ok(Event::Start(ref e)) if e.name() == b"tag2" => {
#[cfg(feature = "asynchronous")]
let text = runtime.block_on(async {
reader
.read_text(b"tag2", &mut Vec::new())
.await
.expect("Cannot decode text value")
});

#[cfg(not(feature = "asynchronous"))]
let text = reader
.read_text(b"tag2", &mut Vec::new())
.expect("Cannot decode text value");
Expand All @@ -49,3 +57,16 @@ fn main() {
buf.clear();
}
}

fn main() {
let xml = "<tag1>text1</tag1><tag1>text2</tag1>\
<tag1>text3</tag1><tag1><tag2>text4</tag2></tag1>";

read_text(xml);

#[cfg(feature = "asynchronous")]
let mut runtime = Runtime::new().expect("Runtime cannot be initialized");

#[cfg(feature = "asynchronous")]
runtime.block_on(async { read_text_async(xml).await });
}
1 change: 1 addition & 0 deletions src/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ mod var;
pub use crate::errors::serialize::DeError;
use crate::{
events::{BytesStart, BytesText, Event},
reader::Decode,
Reader,
};
use serde::de::{self, DeserializeOwned};
Expand Down
2 changes: 1 addition & 1 deletion src/events/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ impl<'a> Attribute<'a> {
/// 1. BytesText::unescaped()
/// 2. Reader::decode(...)
#[cfg(feature = "encoding")]
pub fn unescape_and_decode_without_bom(&self, reader: &impl Decode) -> Result<String> {
pub fn unescape_and_decode_without_bom(&self, reader: &mut impl Decode) -> Result<String> {
let decoded = reader.decode_without_bom(&*self.value);
let unescaped = unescape(decoded.as_bytes()).map_err(Error::EscapeError)?;
String::from_utf8(unescaped.into_owned()).map_err(|e| Error::Utf8(e.utf8_error()))
Expand Down
Loading

0 comments on commit fb763a5

Please sign in to comment.