use std::cell::Cell;
use std::io::{BufRead, BufReader, Read};
use std::rc::Rc;
use quick_xml::Reader;
use super::document::Document;
use super::node::NodeId;
use super::parser::{handle_event, Builder, EventOutcome, PositionSource};
use crate::error::Result;
struct LineTrackingReader<R> {
inner: BufReader<R>,
position: Rc<Cell<(usize, usize)>>,
}
impl<R: Read> LineTrackingReader<R> {
fn new(inner: R) -> (Self, Rc<Cell<(usize, usize)>>) {
let position = Rc::new(Cell::new((1, 1)));
(
Self {
inner: BufReader::new(inner),
position: Rc::clone(&position),
},
position,
)
}
fn advance(position: &Cell<(usize, usize)>, bytes: &[u8]) {
let (mut line, mut column) = position.get();
for &b in bytes {
if b == b'\n' {
line += 1;
column = 1;
} else {
column += 1;
}
}
position.set((line, column));
}
}
impl<R: Read> Read for LineTrackingReader<R> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let n = self.inner.read(buf)?;
Self::advance(&self.position, &buf[..n]);
Ok(n)
}
}
impl<R: Read> BufRead for LineTrackingReader<R> {
fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
self.inner.fill_buf()
}
fn consume(&mut self, amt: usize) {
if let Ok(buf) = self.inner.fill_buf() {
let n = amt.min(buf.len());
Self::advance(&self.position, &buf[..n]);
}
self.inner.consume(amt);
}
}
pub(crate) struct StreamingReader<R: Read> {
reader: Reader<LineTrackingReader<R>>,
buf: Vec<u8>,
builder: Builder<'static>,
open: Vec<NodeId>,
saw_element: bool,
skeleton_len: usize,
record_parent: NodeId,
sibling_counters: std::collections::HashMap<(super::NodeKind, Option<String>, String), usize>,
}
impl<R: Read> StreamingReader<R> {
pub(crate) fn open(source: R) -> Result<Self> {
let (counting, position) = LineTrackingReader::new(source);
let mut reader = Reader::from_reader(counting);
let config = reader.config_mut();
config.trim_text(false);
config.expand_empty_elements = false;
config.check_end_names = true;
config.check_comments = true;
let mut builder = Builder::new(PositionSource::Streaming(position));
let mut open: Vec<NodeId> = vec![builder.doc.root];
let mut saw_element = false;
let mut buf = Vec::new();
loop {
buf.clear();
let event = reader
.read_event_into(&mut buf)
.map_err(|e| builder.error(0, e.to_string()))?;
match handle_event(&mut builder, &mut open, &mut saw_element, 0, event)? {
EventOutcome::Eof => {
return Err(builder.error(0, "document has no element"));
}
EventOutcome::Continue => {}
}
if saw_element {
break;
}
}
let record_parent = *open.last().expect("the root is never popped");
let skeleton_len = builder.doc.nodes.len();
Ok(Self {
reader,
buf,
builder,
open,
saw_element,
skeleton_len,
record_parent,
sibling_counters: std::collections::HashMap::new(),
})
}
pub(crate) fn document(&self) -> &Document {
&self.builder.doc
}
pub(crate) fn next_record(&mut self) -> Result<Option<NodeId>> {
self.builder.doc.nodes.truncate(self.skeleton_len);
self.builder.doc.nodes[self.record_parent.0].children.clear();
let mut record: Option<NodeId> = None;
loop {
self.buf.clear();
let event = self
.reader
.read_event_into(&mut self.buf)
.map_err(|e| self.builder.error(0, e.to_string()))?;
let opens_next_record = record.is_none()
&& self.open.len() == 2
&& matches!(
event,
quick_xml::events::Event::Start(_) | quick_xml::events::Event::Empty(_)
);
match handle_event(
&mut self.builder,
&mut self.open,
&mut self.saw_element,
0,
event,
)? {
EventOutcome::Eof => {
return Err(self.builder.error(0, "unclosed element at end of input"));
}
EventOutcome::Continue => {}
}
if opens_next_record {
record = Some(
*self.builder.doc.nodes[self.record_parent.0]
.children
.last()
.expect("handle_event just pushed this record onto its parent"),
);
}
if self.open.len() == 1 {
return Ok(None);
}
if self.open.len() == 2 && record.is_some() {
break;
}
}
let record = record.expect("open.len() == 2 with a record is only reached after one opened");
self.builder.doc.finalize_subtree(record);
let key = self.sibling_key(record);
let counter = self.sibling_counters.entry(key).or_insert(0);
*counter += 1;
self.builder.doc.set_sibling_position(record, *counter);
Ok(Some(record))
}
pub(crate) fn finish(&mut self) -> Result<()> {
loop {
self.buf.clear();
let event = self
.reader
.read_event_into(&mut self.buf)
.map_err(|e| self.builder.error(0, e.to_string()))?;
if let EventOutcome::Eof = handle_event(
&mut self.builder,
&mut self.open,
&mut self.saw_element,
0,
event,
)? {
break;
}
}
if self.open.len() != 1 {
return Err(self.builder.error(0, "unclosed element at end of input"));
}
Ok(())
}
fn sibling_key(&self, id: NodeId) -> (super::NodeKind, Option<String>, String) {
let document = &self.builder.doc;
let (uri, local) = document
.name(id)
.map_or((None, String::new()), |n| (n.uri.clone(), n.local.clone()));
(document.kind(id), uri, local)
}
}