Skip to content

Commit 75bf989

Browse files
committed
Allow using tokio's AsyncBufRead
1 parent 2032228 commit 75bf989

20 files changed

+2241
-518
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ name = "quick-xml"
33
version = "0.22.0"
44
authors = ["Johann Tuffe <[email protected]>"]
55
description = "High performance xml reader and writer"
6+
edition = "2018"
67

78
documentation = "https://docs.rs/quick-xml"
89
repository = "https://github.com/tafia/quick-xml"
@@ -15,14 +16,17 @@ license = "MIT"
1516
travis-ci = { repository = "tafia/quick-xml" }
1617

1718
[dependencies]
19+
async-recursion = { version = "0.3.2", optional = true }
1820
encoding_rs = { version = "0.8.26", optional = true }
21+
tokio = { version = "0.2.22", features = ["fs", "io-util"], optional = true }
1922
serde = { version = "1.0", optional = true }
2023
memchr = "2.3.4"
2124

2225
[dev-dependencies]
2326
serde = { version = "1.0", features = ["derive"] }
2427
serde-value = "0.7"
2528
regex = "1"
29+
tokio = { version = "0.2.22", features = ["macros", "rt-threaded"] }
2630

2731
[lib]
2832
bench = false
@@ -32,6 +36,7 @@ default = []
3236
encoding = ["encoding_rs"]
3337
serialize = ["serde"]
3438
escape-html = []
39+
asynchronous = ["tokio", "async-recursion"]
3540

3641
[package.metadata.docs.rs]
3742
features = ["serialize"]

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,8 @@ fn crates_io() -> Result<Html, DeError> {
210210

211211
### Credits
212212

213-
This has largely been inspired by [serde-xml-rs](https://github.com/RReverser/serde-xml-rs).
214-
quick-xml follows its convention for deserialization, including the
213+
This has largely been inspired by [serde-xml-rs](https://github.com/RReverser/serde-xml-rs).
214+
quick-xml follows its convention for deserialization, including the
215215
[`$value`](https://github.com/RReverser/serde-xml-rs#parsing-the-value-of-a-tag) special name.
216216

217217
### Parsing the "value" of a tag
@@ -234,6 +234,7 @@ Note that despite not focusing on performance (there are several unecessary copi
234234

235235
- `encoding`: support non utf8 xmls
236236
- `serialize`: support serde `Serialize`/`Deserialize`
237+
- `asynchronous`: support for `AsyncRead`s in `tokio`
237238

238239
## Performance
239240

examples/custom_entities.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use quick_xml::events::Event;
1414
use quick_xml::Reader;
1515
use regex::bytes::Regex;
1616
use std::collections::HashMap;
17+
#[cfg(feature = "asynchronous")]
18+
use tokio::runtime::Runtime;
1719

1820
const DATA: &str = r#"
1921
@@ -33,8 +35,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
3335
let mut custom_entities = HashMap::new();
3436
let entity_re = Regex::new(r#"<!ENTITY\s+([^ \t\r\n]+)\s+"([^"]*)"\s*>"#)?;
3537

38+
#[cfg(feature = "asynchronous")]
39+
let mut runtime = Runtime::new().expect("Runtime cannot be initialized");
40+
3641
loop {
37-
match reader.read_event(&mut buf) {
42+
#[cfg(feature = "asynchronous")]
43+
let event = runtime.block_on(async { reader.read_event(&mut buf).await });
44+
45+
#[cfg(not(feature = "asynchronous"))]
46+
let event = reader.read_event(&mut buf);
47+
48+
match event {
3849
Ok(Event::DocType(ref e)) => {
3950
for cap in entity_re.captures_iter(&e) {
4051
custom_entities.insert(cap[1].to_vec(), cap[2].to_vec());

examples/issue68.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#![allow(unused)]
22

3-
extern crate quick_xml;
4-
53
use quick_xml::events::Event;
64
use quick_xml::Reader;
75
use std::io::Read;
6+
#[cfg(feature = "asynchronous")]
7+
use tokio::runtime::Runtime;
88

99
struct Resource {
1010
etag: String,
@@ -81,8 +81,18 @@ fn parse_report(xml_data: &str) -> Vec<Resource> {
8181
let mut depth = 0;
8282
let mut state = State::MultiStatus;
8383

84+
#[cfg(feature = "asynchronous")]
85+
let mut runtime = Runtime::new().expect("Runtime cannot be initialized");
86+
8487
loop {
85-
match reader.read_namespaced_event(&mut buf, &mut ns_buffer) {
88+
#[cfg(feature = "asynchronous")]
89+
let event = runtime
90+
.block_on(async { reader.read_namespaced_event(&mut buf, &mut ns_buffer).await });
91+
92+
#[cfg(not(feature = "asynchronous"))]
93+
let event = reader.read_namespaced_event(&mut buf, &mut ns_buffer);
94+
95+
match event {
8696
Ok((namespace_value, Event::Start(e))) => {
8797
let namespace_value = namespace_value.unwrap_or_default();
8898
match (depth, state, namespace_value, e.local_name()) {

examples/nested_readers.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
extern crate quick_xml;
21
use quick_xml::events::Event;
32
use quick_xml::Reader;
3+
#[cfg(feature = "asynchronous")]
4+
use tokio::runtime::Runtime;
5+
46
// a structure to capture the rows we've extracted
57
// from a ECMA-376 table in document.xml
68
#[derive(Debug, Clone)]
@@ -16,10 +18,26 @@ fn main() -> Result<(), quick_xml::Error> {
1618
// buffer for nested reader
1719
let mut skip_buf = Vec::new();
1820
let mut count = 0;
21+
22+
#[cfg(feature = "asynchronous")]
23+
let mut runtime = Runtime::new().expect("Runtime cannot be initialized");
24+
25+
#[cfg(feature = "asynchronous")]
26+
let mut reader =
27+
runtime.block_on(async { Reader::from_file("tests/documents/document.xml").await })?;
28+
29+
#[cfg(not(feature = "asynchronous"))]
1930
let mut reader = Reader::from_file("tests/documents/document.xml")?;
31+
2032
let mut found_tables = Vec::new();
2133
loop {
22-
match reader.read_event(&mut buf)? {
34+
#[cfg(feature = "asynchronous")]
35+
let event = runtime.block_on(async { reader.read_event(&mut buf).await })?;
36+
37+
#[cfg(not(feature = "asynchronous"))]
38+
let event = reader.read_event(&mut buf)?;
39+
40+
match event {
2341
Event::Start(element) => match element.name() {
2442
b"w:tbl" => {
2543
count += 1;
@@ -32,7 +50,15 @@ fn main() -> Result<(), quick_xml::Error> {
3250
let mut row_index = 0;
3351
loop {
3452
skip_buf.clear();
35-
match reader.read_event(&mut skip_buf)? {
53+
54+
#[cfg(feature = "asynchronous")]
55+
let event =
56+
runtime.block_on(async { reader.read_event(&mut skip_buf).await })?;
57+
58+
#[cfg(not(feature = "asynchronous"))]
59+
let event = reader.read_event(&mut skip_buf)?;
60+
61+
match event {
3662
Event::Start(element) => match element.name() {
3763
b"w:tr" => {
3864
stats.rows.push(vec![]);

examples/read_texts.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
extern crate quick_xml;
1+
#[cfg(feature = "asynchronous")]
2+
use tokio::runtime::Runtime;
23

34
fn main() {
45
use quick_xml::events::Event;
@@ -13,14 +14,32 @@ fn main() {
1314
let mut txt = Vec::new();
1415
let mut buf = Vec::new();
1516

17+
#[cfg(feature = "asynchronous")]
18+
let mut runtime = Runtime::new().expect("Runtime cannot be initialized");
19+
1620
loop {
17-
match reader.read_event(&mut buf) {
21+
#[cfg(feature = "asynchronous")]
22+
let event = runtime.block_on(async { reader.read_event(&mut buf).await });
23+
24+
#[cfg(not(feature = "asynchronous"))]
25+
let event = reader.read_event(&mut buf);
26+
27+
match event {
1828
Ok(Event::Start(ref e)) if e.name() == b"tag2" => {
19-
txt.push(
29+
#[cfg(feature = "asynchronous")]
30+
let text = runtime.block_on(async {
2031
reader
2132
.read_text(b"tag2", &mut Vec::new())
22-
.expect("Cannot decode text value"),
23-
);
33+
.await
34+
.expect("Cannot decode text value")
35+
});
36+
37+
#[cfg(not(feature = "asynchronous"))]
38+
let text = reader
39+
.read_text(b"tag2", &mut Vec::new())
40+
.expect("Cannot decode text value");
41+
42+
txt.push(text);
2443
println!("{:?}", txt);
2544
}
2645
Ok(Event::Eof) => break, // exits the loop when reaching end of file

src/de/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ mod var;
115115
pub use crate::errors::serialize::DeError;
116116
use crate::{
117117
events::{BytesStart, BytesText, Event},
118+
reader::Decode,
118119
Reader,
119120
};
120121
use serde::de::{self, DeserializeOwned};

src/errors.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub enum Error {
3333
/// Duplicate attribute
3434
DuplicatedAttribute(usize, usize),
3535
/// Escape error
36-
EscapeError(::escape::EscapeError),
36+
Escape(crate::escape::EscapeError),
3737
}
3838

3939
impl From<::std::io::Error> for Error {
@@ -101,7 +101,7 @@ impl std::fmt::Display for Error {
101101
Duplicate attribute at position {1} and {0}",
102102
pos1, pos2
103103
),
104-
Error::EscapeError(e) => write!(f, "{}", e),
104+
Error::Escape(e) => write!(f, "{}", e),
105105
}
106106
}
107107
}
@@ -111,7 +111,7 @@ impl std::error::Error for Error {
111111
match self {
112112
Error::Io(e) => Some(e),
113113
Error::Utf8(e) => Some(e),
114-
Error::EscapeError(e) => Some(e),
114+
Error::Escape(e) => Some(e),
115115
_ => None,
116116
}
117117
}

src/escape.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
//! Pub escape module.
2+
3+
pub(crate) use crate::escapei::{do_unescape, EscapeError};
4+
pub use crate::escapei::{escape, unescape, unescape_with};

src/escapei.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//! Manage xml character escapes
22
3-
use memchr;
43
use std::borrow::Cow;
54
use std::collections::HashMap;
65
use std::ops::Range;
@@ -181,7 +180,7 @@ const fn named_entity(name: &[u8]) -> Option<&str> {
181180
b"amp" => "&",
182181
b"apos" => "'",
183182
b"quot" => "\"",
184-
_ => return None
183+
_ => return None,
185184
};
186185
Some(s)
187186
}
@@ -820,11 +819,9 @@ const fn named_entity(name: &[u8]) -> Option<&str> {
820819
b"mid" | b"VerticalBar" | b"smid" | b"shortmid" => "\u{2223}",
821820
b"nmid" | b"NotVerticalBar" | b"nsmid" | b"nshortmid" => "\u{2224}",
822821
b"par" | b"parallel" | b"DoubleVerticalBar" | b"spar" | b"shortparallel" => "\u{2225}",
823-
b"npar"
824-
| b"nparallel"
825-
| b"NotDoubleVerticalBar"
826-
| b"nspar"
827-
| b"nshortparallel" => "\u{2226}",
822+
b"npar" | b"nparallel" | b"NotDoubleVerticalBar" | b"nspar" | b"nshortparallel" => {
823+
"\u{2226}"
824+
}
828825
b"and" | b"wedge" => "\u{2227}",
829826
b"or" | b"vee" => "\u{2228}",
830827
b"cap" => "\u{2229}",
@@ -1645,13 +1642,13 @@ const fn named_entity(name: &[u8]) -> Option<&str> {
16451642
b"xopf" => "\u{1D56}",
16461643
b"yopf" => "\u{1D56}",
16471644
b"zopf" => "\u{1D56}",
1648-
_ => return None
1645+
_ => return None,
16491646
};
16501647
Some(s)
16511648
}
16521649

16531650
fn push_utf8(out: &mut Vec<u8>, code: char) {
1654-
let mut buf = [0u8; 4];
1651+
let mut buf = [0_u8; 4];
16551652
out.extend_from_slice(code.encode_utf8(&mut buf).as_bytes());
16561653
}
16571654

0 commit comments

Comments
 (0)