diff --git a/Cargo.toml b/Cargo.toml
index 0da6e32e..2de2133d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,6 +16,8 @@ document-features = { version = "0.2", optional = true }
encoding_rs = { version = "0.8", optional = true }
serde = { version = "1.0", optional = true }
memchr = "2.5"
+tokio = { version = "1.19", optional = true, default-features = false, features = ["io-util", "fs"] }
+async-recursion = { version = "1.0", optional = true }
[dev-dependencies]
criterion = "0.3"
@@ -23,6 +25,7 @@ pretty_assertions = "1.2"
regex = "1"
serde = { version = "1.0", features = ["derive"] }
serde-value = "0.7"
+tokio = { version = "1.19", default-features = false, features = ["macros", "rt-multi-thread"] }
[[bench]]
name = "bench"
@@ -94,6 +97,9 @@ serialize = ["serde"]
## Enables support for recognizing all [HTML 5 entities](https://dev.w3.org/html5/html-author/charref)
escape-html = []
+## Enable support for asynchronous reading
+async = ["tokio", "async-recursion"]
+
[package.metadata.docs.rs]
all-features = true
diff --git a/src/reader.rs b/src/reader.rs
index abc1b591..a3fd51cf 100644
--- a/src/reader.rs
+++ b/src/reader.rs
@@ -13,9 +13,13 @@ use crate::name::{LocalName, NamespaceResolver, QName, ResolveResult};
use memchr;
+#[cfg(feature = "async")]
+mod async_reader;
mod io_reader;
mod slice_reader;
+#[cfg(feature = "async")]
+pub use self::async_reader::AsyncReader;
pub use self::{io_reader::IoReader, slice_reader::SliceReader};
/// Possible reader states. The state transition diagram (`true` and `false` shows
@@ -945,7 +949,12 @@ fn detect_encoding(bytes: &[u8]) -> Option<&'static Encoding> {
#[cfg(test)]
mod test {
macro_rules! check {
- ($(let mut $buf:ident = $init:expr;)?) => {
+ ($(let mut $buf:ident = $init:expr; $($await:tt)?)?) => {
+ check!(#[test] {
+ $(let mut $buf = $init; $($await)?)?
+ });
+ };
+ (#[$test:meta] $($async:ident)? { $(let mut $buf:ident = $init:expr; $($await:tt)?)? }) => {
mod read_bytes_until {
use super::input_from_bytes;
// Use Bytes for printing bytes as strings for ASCII range
@@ -953,8 +962,8 @@ mod test {
use pretty_assertions::assert_eq;
/// Checks that search in the empty buffer returns `None`
- #[test]
- fn empty() {
+ #[$test]
+ $($async)? fn empty() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"".as_ref());
@@ -963,6 +972,7 @@ mod test {
assert_eq!(
input
.read_bytes_until(b'*', $(&mut $buf, )? &mut position)
+ $($(.$await)?)?
.unwrap()
.map(Bytes),
None
@@ -972,8 +982,8 @@ mod test {
/// Checks that search in the buffer non-existent value returns entire buffer
/// as a result and set `position` to `len()`
- #[test]
- fn non_existent() {
+ #[$test]
+ $($async)? fn non_existent() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"abcdef".as_ref());
@@ -982,6 +992,7 @@ mod test {
assert_eq!(
input
.read_bytes_until(b'*', $(&mut $buf, )? &mut position)
+ $($(.$await)?)?
.unwrap()
.map(Bytes),
Some(Bytes(b"abcdef"))
@@ -992,8 +1003,8 @@ mod test {
/// Checks that search in the buffer an element that is located in the front of
/// buffer returns empty slice as a result and set `position` to one symbol
/// after match (`1`)
- #[test]
- fn at_the_start() {
+ #[$test]
+ $($async)? fn at_the_start() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"*abcdef".as_ref());
@@ -1002,6 +1013,7 @@ mod test {
assert_eq!(
input
.read_bytes_until(b'*', $(&mut $buf, )? &mut position)
+ $($(.$await)?)?
.unwrap()
.map(Bytes),
Some(Bytes(b""))
@@ -1012,8 +1024,8 @@ mod test {
/// Checks that search in the buffer an element that is located in the middle of
/// buffer returns slice before that symbol as a result and set `position` to one
/// symbol after match
- #[test]
- fn inside() {
+ #[$test]
+ $($async)? fn inside() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"abc*def".as_ref());
@@ -1022,6 +1034,7 @@ mod test {
assert_eq!(
input
.read_bytes_until(b'*', $(&mut $buf, )? &mut position)
+ $($(.$await)?)?
.unwrap()
.map(Bytes),
Some(Bytes(b"abc"))
@@ -1032,8 +1045,8 @@ mod test {
/// Checks that search in the buffer an element that is located in the end of
/// buffer returns slice before that symbol as a result and set `position` to one
/// symbol after match (`len()`)
- #[test]
- fn in_the_end() {
+ #[$test]
+ $($async)? fn in_the_end() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"abcdef*".as_ref());
@@ -1042,6 +1055,7 @@ mod test {
assert_eq!(
input
.read_bytes_until(b'*', $(&mut $buf, )? &mut position)
+ $($(.$await)?)?
.unwrap()
.map(Bytes),
Some(Bytes(b"abcdef"))
@@ -1062,15 +1076,15 @@ mod test {
/// Checks that if input begins like CDATA element, but CDATA start sequence
/// is not finished, parsing ends with an error
- #[test]
+ #[$test]
#[ignore = "start CDATA sequence fully checked outside of `read_bang_element`"]
- fn not_properly_start() {
+ $($async)? fn not_properly_start() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"![]]>other content".as_ref());
// ^= 0
- match input.read_bang_element($(&mut $buf, )? &mut position) {
+ match input.read_bang_element($(&mut $buf, )? &mut position)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "CData" => {}
x => assert!(
false,
@@ -1083,14 +1097,14 @@ mod test {
/// Checks that if CDATA startup sequence was matched, but an end sequence
/// is not found, parsing ends with an error
- #[test]
- fn not_closed() {
+ #[$test]
+ $($async)? fn not_closed() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"![CDATA[other content".as_ref());
// ^= 0
- match input.read_bang_element($(&mut $buf, )? &mut position) {
+ match input.read_bang_element($(&mut $buf, )? &mut position)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "CData" => {}
x => assert!(
false,
@@ -1102,8 +1116,8 @@ mod test {
}
/// Checks that CDATA element without content inside parsed successfully
- #[test]
- fn empty() {
+ #[$test]
+ $($async)? fn empty() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"![CDATA[]]>other content".as_ref());
@@ -1112,6 +1126,7 @@ mod test {
assert_eq!(
input
.read_bang_element($(&mut $buf, )? &mut position)
+ $($(.$await)?)?
.unwrap()
.map(|(ty, data)| (ty, Bytes(data))),
Some((BangType::CData, Bytes(b"![CDATA[")))
@@ -1122,8 +1137,8 @@ mod test {
/// Checks that CDATA element with content parsed successfully.
/// Additionally checks that sequences inside CDATA that may look like
/// a CDATA end sequence do not interrupt CDATA parsing
- #[test]
- fn with_content() {
+ #[$test]
+ $($async)? fn with_content() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"![CDATA[cdata]] ]>content]]>other content]]>".as_ref());
@@ -1132,6 +1147,7 @@ mod test {
assert_eq!(
input
.read_bang_element($(&mut $buf, )? &mut position)
+ $($(.$await)?)?
.unwrap()
.map(|(ty, data)| (ty, Bytes(data))),
Some((BangType::CData, Bytes(b"![CDATA[cdata]] ]>content")))
@@ -1163,15 +1179,15 @@ mod test {
use crate::utils::Bytes;
use pretty_assertions::assert_eq;
- #[test]
+ #[$test]
#[ignore = "start comment sequence fully checked outside of `read_bang_element`"]
- fn not_properly_start() {
+ $($async)? fn not_properly_start() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"!- -->other content".as_ref());
// ^= 0
- match input.read_bang_element($(&mut $buf, )? &mut position) {
+ match input.read_bang_element($(&mut $buf, )? &mut position)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "Comment" => {}
x => assert!(
false,
@@ -1182,14 +1198,14 @@ mod test {
assert_eq!(position, 0);
}
- #[test]
- fn not_properly_end() {
+ #[$test]
+ $($async)? fn not_properly_end() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"!->other content".as_ref());
// ^= 0
- match input.read_bang_element($(&mut $buf, )? &mut position) {
+ match input.read_bang_element($(&mut $buf, )? &mut position)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "Comment" => {}
x => assert!(
false,
@@ -1200,14 +1216,14 @@ mod test {
assert_eq!(position, 0);
}
- #[test]
- fn not_closed1() {
+ #[$test]
+ $($async)? fn not_closed1() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"!--other content".as_ref());
// ^= 0
- match input.read_bang_element($(&mut $buf, )? &mut position) {
+ match input.read_bang_element($(&mut $buf, )? &mut position)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "Comment" => {}
x => assert!(
false,
@@ -1218,14 +1234,14 @@ mod test {
assert_eq!(position, 0);
}
- #[test]
- fn not_closed2() {
+ #[$test]
+ $($async)? fn not_closed2() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"!-->other content".as_ref());
// ^= 0
- match input.read_bang_element($(&mut $buf, )? &mut position) {
+ match input.read_bang_element($(&mut $buf, )? &mut position)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "Comment" => {}
x => assert!(
false,
@@ -1236,14 +1252,14 @@ mod test {
assert_eq!(position, 0);
}
- #[test]
- fn not_closed3() {
+ #[$test]
+ $($async)? fn not_closed3() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"!--->other content".as_ref());
// ^= 0
- match input.read_bang_element($(&mut $buf, )? &mut position) {
+ match input.read_bang_element($(&mut $buf, )? &mut position)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "Comment" => {}
x => assert!(
false,
@@ -1254,8 +1270,8 @@ mod test {
assert_eq!(position, 0);
}
- #[test]
- fn empty() {
+ #[$test]
+ $($async)? fn empty() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"!---->other content".as_ref());
@@ -1264,6 +1280,7 @@ mod test {
assert_eq!(
input
.read_bang_element($(&mut $buf, )? &mut position)
+ $($(.$await)?)?
.unwrap()
.map(|(ty, data)| (ty, Bytes(data))),
Some((BangType::Comment, Bytes(b"!----")))
@@ -1271,8 +1288,8 @@ mod test {
assert_eq!(position, 6);
}
- #[test]
- fn with_content() {
+ #[$test]
+ $($async)? fn with_content() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"!--->comment<--->other content".as_ref());
@@ -1281,6 +1298,7 @@ mod test {
assert_eq!(
input
.read_bang_element($(&mut $buf, )? &mut position)
+ $($(.$await)?)?
.unwrap()
.map(|(ty, data)| (ty, Bytes(data))),
Some((BangType::Comment, Bytes(b"!--->comment<---")))
@@ -1299,14 +1317,14 @@ mod test {
use crate::utils::Bytes;
use pretty_assertions::assert_eq;
- #[test]
- fn not_properly_start() {
+ #[$test]
+ $($async)? fn not_properly_start() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"!D other content".as_ref());
// ^= 0
- match input.read_bang_element($(&mut $buf, )? &mut position) {
+ match input.read_bang_element($(&mut $buf, )? &mut position)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {}
x => assert!(
false,
@@ -1317,14 +1335,14 @@ mod test {
assert_eq!(position, 0);
}
- #[test]
- fn without_space() {
+ #[$test]
+ $($async)? fn without_space() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"!DOCTYPEother content".as_ref());
// ^= 0
- match input.read_bang_element($(&mut $buf, )? &mut position) {
+ match input.read_bang_element($(&mut $buf, )? &mut position)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {}
x => assert!(
false,
@@ -1335,8 +1353,8 @@ mod test {
assert_eq!(position, 0);
}
- #[test]
- fn empty() {
+ #[$test]
+ $($async)? fn empty() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"!DOCTYPE>other content".as_ref());
@@ -1345,6 +1363,7 @@ mod test {
assert_eq!(
input
.read_bang_element($(&mut $buf, )? &mut position)
+ $($(.$await)?)?
.unwrap()
.map(|(ty, data)| (ty, Bytes(data))),
Some((BangType::DocType, Bytes(b"!DOCTYPE")))
@@ -1352,14 +1371,14 @@ mod test {
assert_eq!(position, 9);
}
- #[test]
- fn not_closed() {
+ #[$test]
+ $($async)? fn not_closed() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"!DOCTYPE other content".as_ref());
// ^= 0
- match input.read_bang_element($(&mut $buf, )? &mut position) {
+ match input.read_bang_element($(&mut $buf, )? &mut position)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {}
x => assert!(
false,
@@ -1378,14 +1397,14 @@ mod test {
use crate::utils::Bytes;
use pretty_assertions::assert_eq;
- #[test]
- fn not_properly_start() {
+ #[$test]
+ $($async)? fn not_properly_start() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"!d other content".as_ref());
// ^= 0
- match input.read_bang_element($(&mut $buf, )? &mut position) {
+ match input.read_bang_element($(&mut $buf, )? &mut position)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {}
x => assert!(
false,
@@ -1396,14 +1415,14 @@ mod test {
assert_eq!(position, 0);
}
- #[test]
- fn without_space() {
+ #[$test]
+ $($async)? fn without_space() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"!doctypeother content".as_ref());
// ^= 0
- match input.read_bang_element($(&mut $buf, )? &mut position) {
+ match input.read_bang_element($(&mut $buf, )? &mut position)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {}
x => assert!(
false,
@@ -1414,8 +1433,8 @@ mod test {
assert_eq!(position, 0);
}
- #[test]
- fn empty() {
+ #[$test]
+ $($async)? fn empty() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"!doctype>other content".as_ref());
@@ -1424,6 +1443,7 @@ mod test {
assert_eq!(
input
.read_bang_element($(&mut $buf, )? &mut position)
+ $($(.$await)?)?
.unwrap()
.map(|(ty, data)| (ty, Bytes(data))),
Some((BangType::DocType, Bytes(b"!doctype")))
@@ -1431,14 +1451,14 @@ mod test {
assert_eq!(position, 9);
}
- #[test]
- fn not_closed() {
+ #[$test]
+ $($async)? fn not_closed() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"!doctype other content".as_ref());
// ^= 0
- match input.read_bang_element($(&mut $buf, )? &mut position) {
+ match input.read_bang_element($(&mut $buf, )? &mut position)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {}
x => assert!(
false,
@@ -1458,14 +1478,14 @@ mod test {
use pretty_assertions::assert_eq;
/// Checks that nothing was read from empty buffer
- #[test]
- fn empty() {
+ #[$test]
+ $($async)? fn empty() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"".as_ref());
// ^= 0
- assert_eq!(input.read_element($(&mut $buf, )? &mut position).unwrap().map(Bytes), None);
+ assert_eq!(input.read_element($(&mut $buf, )? &mut position)$($(.$await)?)?.unwrap().map(Bytes), None);
assert_eq!(position, 0);
}
@@ -1474,71 +1494,71 @@ mod test {
use crate::utils::Bytes;
use pretty_assertions::assert_eq;
- #[test]
- fn empty_tag() {
+ #[$test]
+ $($async)? fn empty_tag() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b">".as_ref());
// ^= 1
assert_eq!(
- input.read_element($(&mut $buf, )? &mut position).unwrap().map(Bytes),
+ input.read_element($(&mut $buf, )? &mut position)$($(.$await)?)?.unwrap().map(Bytes),
Some(Bytes(b""))
);
assert_eq!(position, 1);
}
- #[test]
- fn normal() {
+ #[$test]
+ $($async)? fn normal() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"tag>".as_ref());
// ^= 4
assert_eq!(
- input.read_element($(&mut $buf, )? &mut position).unwrap().map(Bytes),
+ input.read_element($(&mut $buf, )? &mut position)$($(.$await)?)?.unwrap().map(Bytes),
Some(Bytes(b"tag"))
);
assert_eq!(position, 4);
}
- #[test]
- fn empty_ns_empty_tag() {
+ #[$test]
+ $($async)? fn empty_ns_empty_tag() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b":>".as_ref());
// ^= 2
assert_eq!(
- input.read_element($(&mut $buf, )? &mut position).unwrap().map(Bytes),
+ input.read_element($(&mut $buf, )? &mut position)$($(.$await)?)?.unwrap().map(Bytes),
Some(Bytes(b":"))
);
assert_eq!(position, 2);
}
- #[test]
- fn empty_ns() {
+ #[$test]
+ $($async)? fn empty_ns() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b":tag>".as_ref());
// ^= 5
assert_eq!(
- input.read_element($(&mut $buf, )? &mut position).unwrap().map(Bytes),
+ input.read_element($(&mut $buf, )? &mut position)$($(.$await)?)?.unwrap().map(Bytes),
Some(Bytes(b":tag"))
);
assert_eq!(position, 5);
}
- #[test]
- fn with_attributes() {
+ #[$test]
+ $($async)? fn with_attributes() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(br#"tag attr-1=">" attr2 = '>' 3attr>"#.as_ref());
// ^= 38
assert_eq!(
- input.read_element($(&mut $buf, )? &mut position).unwrap().map(Bytes),
+ input.read_element($(&mut $buf, )? &mut position)$($(.$await)?)?.unwrap().map(Bytes),
Some(Bytes(br#"tag attr-1=">" attr2 = '>' 3attr"#))
);
assert_eq!(position, 38);
@@ -1550,71 +1570,71 @@ mod test {
use crate::utils::Bytes;
use pretty_assertions::assert_eq;
- #[test]
- fn empty_tag() {
+ #[$test]
+ $($async)? fn empty_tag() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"/>".as_ref());
// ^= 2
assert_eq!(
- input.read_element($(&mut $buf, )? &mut position).unwrap().map(Bytes),
+ input.read_element($(&mut $buf, )? &mut position)$($(.$await)?)?.unwrap().map(Bytes),
Some(Bytes(b"/"))
);
assert_eq!(position, 2);
}
- #[test]
- fn normal() {
+ #[$test]
+ $($async)? fn normal() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b"tag/>".as_ref());
// ^= 5
assert_eq!(
- input.read_element($(&mut $buf, )? &mut position).unwrap().map(Bytes),
+ input.read_element($(&mut $buf, )? &mut position)$($(.$await)?)?.unwrap().map(Bytes),
Some(Bytes(b"tag/"))
);
assert_eq!(position, 5);
}
- #[test]
- fn empty_ns_empty_tag() {
+ #[$test]
+ $($async)? fn empty_ns_empty_tag() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b":/>".as_ref());
// ^= 3
assert_eq!(
- input.read_element($(&mut $buf, )? &mut position).unwrap().map(Bytes),
+ input.read_element($(&mut $buf, )? &mut position)$($(.$await)?)?.unwrap().map(Bytes),
Some(Bytes(b":/"))
);
assert_eq!(position, 3);
}
- #[test]
- fn empty_ns() {
+ #[$test]
+ $($async)? fn empty_ns() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(b":tag/>".as_ref());
// ^= 6
assert_eq!(
- input.read_element($(&mut $buf, )? &mut position).unwrap().map(Bytes),
+ input.read_element($(&mut $buf, )? &mut position)$($(.$await)?)?.unwrap().map(Bytes),
Some(Bytes(b":tag/"))
);
assert_eq!(position, 6);
}
- #[test]
- fn with_attributes() {
+ #[$test]
+ $($async)? fn with_attributes() {
$(let mut $buf = $init;)?
let mut position = 0;
let mut input = input_from_bytes(br#"tag attr-1="/>" attr2 = '/>' 3attr/>"#.as_ref());
// ^= 41
assert_eq!(
- input.read_element($(&mut $buf, )? &mut position).unwrap().map(Bytes),
+ input.read_element($(&mut $buf, )? &mut position)$($(.$await)?)?.unwrap().map(Bytes),
Some(Bytes(br#"tag attr-1="/>" attr2 = '/>' 3attr/"#))
);
assert_eq!(position, 41);
@@ -1626,13 +1646,13 @@ mod test {
use super::reader_from_str;
use crate::errors::Error;
- #[test]
- fn cdata() {
+ #[$test]
+ $($async)? fn cdata() {
let doc = "![]]>";
let mut reader = reader_from_str(doc);
$(let mut $buf = $init;)?
- match reader.read_until_close($(&mut $buf)?) {
+ match reader.read_until_close($(&mut $buf)?)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "CData" => {}
x => assert!(
false,
@@ -1642,13 +1662,13 @@ mod test {
}
}
- #[test]
- fn comment() {
+ #[$test]
+ $($async)? fn comment() {
let doc = "!- -->";
let mut reader = reader_from_str(doc);
$(let mut $buf = $init;)?
- match reader.read_until_close($(&mut $buf)?) {
+ match reader.read_until_close($(&mut $buf)?)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "Comment" => {}
x => assert!(
false,
@@ -1658,13 +1678,13 @@ mod test {
}
}
- #[test]
- fn doctype_uppercase() {
+ #[$test]
+ $($async)? fn doctype_uppercase() {
let doc = "!D>";
let mut reader = reader_from_str(doc);
$(let mut $buf = $init;)?
- match reader.read_until_close($(&mut $buf)?) {
+ match reader.read_until_close($(&mut $buf)?)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {}
x => assert!(
false,
@@ -1674,13 +1694,13 @@ mod test {
}
}
- #[test]
- fn doctype_lowercase() {
+ #[$test]
+ $($async)? fn doctype_lowercase() {
let doc = "!d>";
let mut reader = reader_from_str(doc);
$(let mut $buf = $init;)?
- match reader.read_until_close($(&mut $buf)?) {
+ match reader.read_until_close($(&mut $buf)?)$($(.$await)?)? {
Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {}
x => assert!(
false,
@@ -1697,63 +1717,63 @@ mod test {
use crate::events::{BytesCData, BytesDecl, BytesEnd, BytesStart, BytesText, Event};
use pretty_assertions::assert_eq;
- #[test]
- fn start_text() {
+ #[$test]
+ $($async)? fn start_text() {
let mut reader = reader_from_str("bom");
$(let mut $buf = $init;)?
assert_eq!(
- reader.read_event_impl($(&mut $buf)?).unwrap(),
+ reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap(),
Event::StartText(BytesText::from_escaped(b"bom".as_ref()).into())
);
}
- #[test]
- fn declaration() {
+ #[$test]
+ $($async)? fn declaration() {
let mut reader = reader_from_str("");
$(let mut $buf = $init;)?
assert_eq!(
- reader.read_event_impl($(&mut $buf)?).unwrap(),
+ reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap(),
Event::Decl(BytesDecl::from_start(BytesStart::borrowed(b"xml ", 3)))
);
}
- #[test]
- fn doctype() {
+ #[$test]
+ $($async)? fn doctype() {
let mut reader = reader_from_str("");
$(let mut $buf = $init;)?
assert_eq!(
- reader.read_event_impl($(&mut $buf)?).unwrap(),
+ reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap(),
Event::DocType(BytesText::from_escaped(b"x".as_ref()))
);
}
- #[test]
- fn processing_instruction() {
+ #[$test]
+ $($async)? fn processing_instruction() {
let mut reader = reader_from_str("");
$(let mut $buf = $init;)?
assert_eq!(
- reader.read_event_impl($(&mut $buf)?).unwrap(),
+ reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap(),
Event::PI(BytesText::from_escaped(b"xml-stylesheet".as_ref()))
);
}
- #[test]
- fn start() {
+ #[$test]
+ $($async)? fn start() {
let mut reader = reader_from_str("");
$(let mut $buf = $init;)?
assert_eq!(
- reader.read_event_impl($(&mut $buf)?).unwrap(),
+ reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap(),
Event::Start(BytesStart::borrowed_name(b"tag"))
);
}
- #[test]
- fn end() {
+ #[$test]
+ $($async)? fn end() {
let mut reader = reader_from_str("");
// Because we expect invalid XML, do not check that
// the end name paired with the start name
@@ -1761,68 +1781,68 @@ mod test {
$(let mut $buf = $init;)?
assert_eq!(
- reader.read_event_impl($(&mut $buf)?).unwrap(),
+ reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap(),
Event::End(BytesEnd::borrowed(b"tag"))
);
}
- #[test]
- fn empty() {
+ #[$test]
+ $($async)? fn empty() {
let mut reader = reader_from_str("");
$(let mut $buf = $init;)?
assert_eq!(
- reader.read_event_impl($(&mut $buf)?).unwrap(),
+ reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap(),
Event::Empty(BytesStart::borrowed_name(b"tag"))
);
}
/// Text event cannot be generated without preceding event of another type
- #[test]
- fn text() {
+ #[$test]
+ $($async)? fn text() {
let mut reader = reader_from_str("text");
$(let mut $buf = $init;)?
assert_eq!(
- reader.read_event_impl($(&mut $buf)?).unwrap(),
+ reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap(),
Event::Empty(BytesStart::borrowed_name(b"tag"))
);
assert_eq!(
- reader.read_event_impl($(&mut $buf)?).unwrap(),
+ reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap(),
Event::Text(BytesText::from_escaped(b"text".as_ref()))
);
}
- #[test]
- fn cdata() {
+ #[$test]
+ $($async)? fn cdata() {
let mut reader = reader_from_str("");
$(let mut $buf = $init;)?
assert_eq!(
- reader.read_event_impl($(&mut $buf)?).unwrap(),
+ reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap(),
Event::CData(BytesCData::from_str(""))
);
}
- #[test]
- fn comment() {
+ #[$test]
+ $($async)? fn comment() {
let mut reader = reader_from_str("");
$(let mut $buf = $init;)?
assert_eq!(
- reader.read_event_impl($(&mut $buf)?).unwrap(),
+ reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap(),
Event::Comment(BytesText::from_escaped(b"".as_ref()))
);
}
- #[test]
- fn eof() {
+ #[$test]
+ $($async)? fn eof() {
let mut reader = reader_from_str("");
$(let mut $buf = $init;)?
assert_eq!(
- reader.read_event_impl($(&mut $buf)?).unwrap(),
+ reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap(),
Event::Eof
);
}
@@ -1841,35 +1861,35 @@ mod test {
use pretty_assertions::assert_eq;
/// Checks that encoding is detected by BOM and changed after XML declaration
- #[test]
- fn bom_detected() {
+ #[$test]
+ $($async)? fn bom_detected() {
let mut reader = reader_from_bytes(b"\xFF\xFE");
$(let mut $buf = $init;)?
assert_eq!(reader.decoder().encoding(), UTF_8);
- reader.read_event_impl($(&mut $buf)?).unwrap();
+ reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap();
assert_eq!(reader.decoder().encoding(), UTF_16LE);
- reader.read_event_impl($(&mut $buf)?).unwrap();
+ reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap();
assert_eq!(reader.decoder().encoding(), WINDOWS_1251);
- assert_eq!(reader.read_event_impl($(&mut $buf)?).unwrap(), Event::Eof);
+ assert_eq!(reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap(), Event::Eof);
}
/// Checks that encoding is changed by XML declaration, but only once
- #[test]
- fn xml_declaration() {
+ #[$test]
+ $($async)? fn xml_declaration() {
let mut reader = reader_from_bytes(b"");
$(let mut $buf = $init;)?
assert_eq!(reader.decoder().encoding(), UTF_8);
- reader.read_event_impl($(&mut $buf)?).unwrap();
+ reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap();
assert_eq!(reader.decoder().encoding(), UTF_16LE);
- reader.read_event_impl($(&mut $buf)?).unwrap();
+ reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap();
assert_eq!(reader.decoder().encoding(), UTF_16LE);
- assert_eq!(reader.read_event_impl($(&mut $buf)?).unwrap(), Event::Eof);
+ assert_eq!(reader.read_event_impl($(&mut $buf)?)$($(.$await)?)?.unwrap(), Event::Eof);
}
}
}
diff --git a/src/reader/async_reader.rs b/src/reader/async_reader.rs
new file mode 100644
index 00000000..ccdef053
--- /dev/null
+++ b/src/reader/async_reader.rs
@@ -0,0 +1,691 @@
+use std::{
+ future::Future,
+ ops::{Deref, DerefMut},
+ path::Path,
+};
+
+use async_recursion::async_recursion;
+use tokio::{
+ fs::File,
+ io::{self, AsyncBufRead, AsyncBufReadExt, AsyncRead, BufReader},
+};
+
+use crate::{
+ events::{BytesText, Event},
+ name::{QName, ResolveResult},
+ Error, Result,
+};
+
+#[cfg(feature = "encoding")]
+use super::{detect_encoding, EncodingRef};
+use super::{is_whitespace, BangType, InnerReader, ReadElementState, Reader, TagState};
+
+/// A struct for handling reading functions based on reading from a [`BufRead`].
+#[derive(Debug, Clone)]
+pub struct AsyncReader(R);
+
+impl Deref for AsyncReader {
+ type Target = R;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl DerefMut for AsyncReader {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+impl InnerReader for AsyncReader {
+ type Reader = R;
+
+ fn into_inner(self) -> Self::Reader {
+ self.0
+ }
+}
+
+/// Private reading functions.
+impl AsyncReader {
+ #[inline]
+ async fn read_bytes_until<'buf>(
+ &mut self,
+ byte: u8,
+ buf: &'buf mut Vec,
+ position: &mut usize,
+ ) -> Result