use tokio::io::AsyncBufReadExt as _;
use super::event::{BorrowedEvent, Event};
use super::machine::Core;
use crate::Limits;
use crate::error::{Error, ErrorKind, Result};
use crate::external::ExternalSource;
#[cfg(feature = "std")]
use std::io;
const CHUNK: usize = 8192;
pub struct AsyncReader<'e, R> {
core: Core<'e>,
inner: R,
}
impl<R> core::fmt::Debug for AsyncReader<'_, R> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("AsyncReader")
.field("core", &self.core)
.finish_non_exhaustive()
}
}
impl<'e, R: tokio::io::AsyncBufRead + Unpin> AsyncReader<'e, R> {
pub async fn from_reader(reader: R) -> Result<Self> {
Self::from_reader_with(reader, Limits::default()).await
}
pub async fn from_reader_with(reader: R, limits: Limits) -> Result<Self> {
let mut reader = Self {
core: Core::with_limits(limits),
inner: reader,
};
while !reader.core.settled() && reader.core.needs_input() {
reader.fill().await?;
}
Ok(reader)
}
#[must_use]
pub fn with_external(
mut self,
external: &'e (dyn ExternalSource + Sync),
) -> Self {
self.core = self.core.with_external(external);
self
}
pub async fn next_event(&mut self) -> Result<Option<Event>> {
loop {
while self.core.needs_input() {
self.fill().await?;
}
self.core.prepare()?;
if !self.core.needs_input() {
break;
}
}
#[cfg(feature = "tracing")]
let _span =
tracing::trace_span!("oxml.stream.async_next_event").entered();
Ok(self.core.next_event()?.map(BorrowedEvent::into_owned))
}
#[must_use]
pub fn offset(&self) -> usize {
self.core.offset()
}
async fn fill(&mut self) -> Result<()> {
loop {
let outcome = match self.inner.fill_buf().await {
Ok([]) => self.core.feed_eof(),
Ok(buf) => {
let take = buf.len().min(CHUNK);
let fed = self.core.feed(&buf[..take]);
self.inner.consume(take);
fed
}
Err(e) if e.kind() == io::ErrorKind::Interrupted => {
continue;
}
Err(e) => {
self.core.fail();
Err(Error::new(
ErrorKind::Io(e.to_string()),
self.core.end_offset(),
))
}
};
return outcome;
}
}
}