use std::any::Any;
use std::io::BufRead;
use std::sync::Arc;
use compact_str::CompactString;
use quick_xml::Reader;
use quick_xml::events::Event;
use crate::error::Result;
use crate::namespace::Namespace;
use crate::position::PositionTrackingReader;
#[derive(Debug, Default)]
pub(crate) struct StringInterner {
cache: rustc_hash::FxHashMap<Box<str>, Arc<str>>,
}
impl StringInterner {
pub(crate) fn new() -> Self {
Self {
cache: rustc_hash::FxHashMap::default(),
}
}
pub(crate) fn intern(&mut self, s: &str) -> Arc<str> {
if let Some(interned) = self.cache.get(s) {
Arc::clone(interned)
} else {
let arc: Arc<str> = Arc::from(s);
self.cache.insert(s.into(), Arc::clone(&arc));
arc
}
}
#[allow(dead_code)]
fn len(&self) -> usize {
self.cache.len()
}
}
#[derive(Debug, Clone)]
pub enum XmlEvent {
StartElement {
name: Arc<str>,
prefix: Option<Arc<str>>,
namespace: Option<String>,
attributes: Vec<(CompactString, CompactString)>,
namespace_decls: Vec<Namespace>,
line: Option<usize>,
column: Option<usize>,
},
EndElement {
name: Arc<str>,
prefix: Option<Arc<str>>,
},
Text(String),
CData(String),
Comment(String),
ProcessingInstruction {
target: String,
content: Option<String>,
},
Declaration {
version: Option<String>,
encoding: Option<String>,
standalone: Option<bool>,
},
Eof,
}
#[derive(Debug)]
pub(crate) enum RawEvent<'a> {
StartElement {
name: &'a str,
prefix: Option<&'a str>,
attributes: &'a [(&'a str, std::borrow::Cow<'a, str>)],
namespace_decls: &'a [Namespace],
line: Option<usize>,
column: Option<usize>,
},
EndElement {
name: &'a str,
prefix: Option<&'a str>,
},
Text(&'a str),
CData(&'a str),
Comment(&'a str),
ProcessingInstruction {
target: &'a str,
content: Option<&'a str>,
},
Declaration {
version: Option<String>,
encoding: Option<String>,
standalone: Option<bool>,
},
Eof,
}
impl RawEvent<'_> {
pub(crate) fn to_xml_event(&self, interner: &mut StringInterner) -> XmlEvent {
match self {
RawEvent::StartElement {
name,
prefix,
attributes,
namespace_decls,
line,
column,
} => XmlEvent::StartElement {
name: interner.intern(name),
prefix: prefix.map(|p| interner.intern(p)),
namespace: None,
attributes: attributes
.iter()
.map(|(k, v)| (CompactString::from(*k), CompactString::from(v.as_ref())))
.collect(),
namespace_decls: namespace_decls.to_vec(),
line: *line,
column: *column,
},
RawEvent::EndElement { name, prefix } => XmlEvent::EndElement {
name: interner.intern(name),
prefix: prefix.map(|p| interner.intern(p)),
},
RawEvent::Text(t) => XmlEvent::Text(t.to_string()),
RawEvent::CData(t) => XmlEvent::CData(t.to_string()),
RawEvent::Comment(t) => XmlEvent::Comment(t.to_string()),
RawEvent::ProcessingInstruction { target, content } => {
XmlEvent::ProcessingInstruction {
target: target.to_string(),
content: content.map(|c| c.to_string()),
}
}
RawEvent::Declaration {
version,
encoding,
standalone,
} => XmlEvent::Declaration {
version: version.clone(),
encoding: encoding.clone(),
standalone: *standalone,
},
RawEvent::Eof => XmlEvent::Eof,
}
}
}
pub(crate) trait XmlEventHandler: Send + Any {
fn handle(&mut self, event: &RawEvent<'_>) -> Result<()>;
fn finish(&mut self) -> Result<()> {
Ok(())
}
fn as_any(self: Box<Self>) -> Box<dyn Any>;
}
pub(crate) struct StreamingParser<R: BufRead> {
reader: Reader<PositionTrackingReader<R>>,
handlers: Vec<Box<dyn XmlEventHandler>>,
entities: std::collections::HashMap<String, String>,
}
impl<R: BufRead> StreamingParser<R> {
pub fn new(reader: R) -> Self {
let position_reader = PositionTrackingReader::new(reader);
let mut xml_reader = Reader::from_reader(position_reader);
xml_reader.config_mut().trim_text(false);
xml_reader.config_mut().expand_empty_elements = true;
Self {
reader: xml_reader,
handlers: Vec::new(),
entities: std::collections::HashMap::new(),
}
}
fn current_line(&self) -> usize {
self.reader.get_ref().line()
}
fn current_column(&self) -> usize {
self.reader.get_ref().column()
}
pub fn add_handler(&mut self, handler: Box<dyn XmlEventHandler>) {
self.handlers.push(handler);
}
pub fn into_handlers(self) -> Vec<Box<dyn XmlEventHandler>> {
self.handlers
}
fn drive_loop<F>(&mut self, mut on_event: F) -> Result<()>
where
F: FnMut(&RawEvent<'_>) -> Result<()>,
{
let mut buffer = Vec::with_capacity(8 * 1024);
let mut checker = crate::parser::checks::WellformedChecker::new();
loop {
let event_result = self.reader.read_event_into(&mut buffer);
let line = self.current_line();
let column = self.current_column();
match event_result {
Ok(Event::Start(ref e)) => {
checker.start(e)?;
let (name, prefix, attributes, namespace_decls) =
split_start_event(e, &self.entities)?;
on_event(&RawEvent::StartElement {
name,
prefix,
attributes: &attributes,
namespace_decls: &namespace_decls,
line: Some(line),
column: Some(column),
})?;
}
Ok(Event::Empty(ref e)) => {
checker.start(e)?;
let (name, prefix, attributes, namespace_decls) =
split_start_event(e, &self.entities)?;
on_event(&RawEvent::StartElement {
name,
prefix,
attributes: &attributes,
namespace_decls: &namespace_decls,
line: Some(line),
column: Some(column),
})?;
checker.end(name)?;
on_event(&RawEvent::EndElement { name, prefix })?;
}
Ok(Event::End(ref e)) => {
let qname = e.name();
let full_name = std::str::from_utf8(qname.as_ref())?;
checker.end(full_name)?;
let (prefix, name) = crate::namespace::split_qname(full_name);
on_event(&RawEvent::EndElement { name, prefix })?;
}
Ok(Event::Text(ref e)) => {
checker.text(std::str::from_utf8(e.as_ref())?)?;
let text = e
.unescape_with(|name| {
self.entities
.get(name)
.map(String::as_str)
.or_else(|| quick_xml::escape::resolve_predefined_entity(name))
})
.map_err(|e| crate::parser::error::ParseError::TextDecodeError {
message: e.to_string(),
})?;
if !text.is_empty() {
on_event(&RawEvent::Text(&text))?;
}
}
Ok(Event::CData(ref e)) => {
let text = std::str::from_utf8(e.as_ref())?;
checker.cdata(text)?;
on_event(&RawEvent::CData(text))?;
}
Ok(Event::Comment(ref e)) => {
let text = std::str::from_utf8(e.as_ref())?;
checker.comment(text)?;
on_event(&RawEvent::Comment(text))?;
}
Ok(Event::PI(ref e)) => {
let content = std::str::from_utf8(e.as_ref())?;
checker.pi(content)?;
let mut parts = content.splitn(2, char::is_whitespace);
let target = parts.next().unwrap_or("");
let pi_content = parts.next().map(str::trim);
on_event(&RawEvent::ProcessingInstruction {
target,
content: pi_content,
})?;
}
Ok(Event::Decl(ref e)) => {
checker.decl(std::str::from_utf8(e.as_ref())?)?;
let version = e
.version()
.ok()
.map(|v| String::from_utf8_lossy(v.as_ref()).into_owned());
let encoding = e
.encoding()
.and_then(|r| r.ok())
.map(|v| String::from_utf8_lossy(v.as_ref()).into_owned());
let standalone = e
.standalone()
.and_then(|r| r.ok())
.map(|v| v.as_ref() == b"yes");
on_event(&RawEvent::Declaration {
version,
encoding,
standalone,
})?;
}
Ok(Event::DocType(ref e)) => {
if let Ok(text) = std::str::from_utf8(e.as_ref()) {
checker.doctype(text)?;
self.entities = crate::parser::entities::parse_internal_entities(text);
}
}
Ok(Event::Eof) => {
checker.eof()?;
on_event(&RawEvent::Eof)?;
break;
}
Err(e) => {
return Err(crate::parser::error::ParseError::AtPosition {
position: self.reader.get_ref().byte_offset() as u64,
message: e.to_string(),
}
.into());
}
}
buffer.clear();
}
Ok(())
}
pub fn parse(&mut self) -> Result<()> {
let mut handlers = std::mem::take(&mut self.handlers);
let result = self
.drive_loop(|event| {
for handler in handlers.iter_mut() {
handler.handle(event)?;
}
Ok(())
})
.and_then(|()| {
for handler in handlers.iter_mut() {
handler.finish()?;
}
Ok(())
});
self.handlers = handlers;
result
}
pub fn for_each_event<F>(&mut self, mut on_event: F) -> Result<()>
where
F: FnMut(&XmlEvent) -> Result<()>,
{
let mut interner = StringInterner::new();
self.drive_loop(|raw| on_event(&raw.to_xml_event(&mut interner)))
}
}
#[allow(clippy::type_complexity)]
fn split_start_event<'a>(
e: &'a quick_xml::events::BytesStart<'a>,
entities: &'a std::collections::HashMap<String, String>,
) -> Result<(
&'a str,
Option<&'a str>,
smallvec::SmallVec<[(&'a str, std::borrow::Cow<'a, str>); 8]>,
smallvec::SmallVec<[Namespace; 2]>,
)> {
let full_name = std::str::from_utf8(e.name().into_inner())?;
let (prefix, name) = crate::namespace::split_qname(full_name);
let mut namespace_decls: smallvec::SmallVec<[Namespace; 2]> = smallvec::SmallVec::new();
let mut attributes: smallvec::SmallVec<[(&str, std::borrow::Cow<str>); 8]> =
smallvec::SmallVec::new();
for attr_result in e.attributes() {
let attr = attr_result?;
let key = std::str::from_utf8(attr.key.into_inner())?;
let value = attr
.unescape_value_with(|name| {
entities
.get(name)
.map(String::as_str)
.or_else(|| quick_xml::escape::resolve_predefined_entity(name))
})
.map_err(|e| crate::parser::error::ParseError::AttributeDecodeError {
message: e.to_string(),
})?;
if key == "xmlns" {
namespace_decls.push(Namespace::default_ns(value.as_ref()));
} else if let Some(ns_prefix) = key.strip_prefix("xmlns:") {
namespace_decls.push(Namespace::new(ns_prefix, value.as_ref()));
} else {
attributes.push((key, value));
}
}
Ok((name, prefix, attributes, namespace_decls))
}
#[cfg(test)]
struct EventCollector {
events: Vec<XmlEvent>,
interner: StringInterner,
}
#[cfg(test)]
impl EventCollector {
fn new() -> Self {
Self {
events: Vec::new(),
interner: StringInterner::new(),
}
}
fn into_events(self) -> Vec<XmlEvent> {
self.events
}
}
#[cfg(test)]
impl XmlEventHandler for EventCollector {
fn handle(&mut self, event: &RawEvent<'_>) -> Result<()> {
self.events.push(event.to_xml_event(&mut self.interner));
Ok(())
}
fn as_any(self: Box<Self>) -> Box<dyn Any> {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_streaming_parser() {
let xml = r#"<root attr="value"><child>text</child></root>"#;
let mut parser = StreamingParser::new(xml.as_bytes());
let collector = EventCollector::new();
parser.add_handler(Box::new(collector));
parser.parse().unwrap();
}
#[test]
fn test_event_collector() {
let mut collector = EventCollector::new();
collector
.handle(&RawEvent::StartElement {
name: "root",
prefix: None,
attributes: &[],
namespace_decls: &[],
line: Some(1),
column: Some(1),
})
.unwrap();
collector
.handle(&RawEvent::StartElement {
name: "child",
prefix: None,
attributes: &[],
namespace_decls: &[],
line: Some(1),
column: Some(1),
})
.unwrap();
collector
.handle(&RawEvent::EndElement {
name: "child",
prefix: None,
})
.unwrap();
collector
.handle(&RawEvent::EndElement {
name: "root",
prefix: None,
})
.unwrap();
collector.handle(&RawEvent::Eof).unwrap();
let events = collector.into_events();
assert_eq!(events.len(), 5);
}
}