use std::borrow::Cow;
use std::mem::replace;
use std::ops::{Deref, Range};
use crate::de::resolver::{EntityResolver, PredefinedEntityResolver};
use crate::de::xml_read::XmlRead;
use crate::encoding::Decoder;
use crate::errors::serialize::DeError;
use crate::escape::{EscapeError, parse_number};
use crate::events::{BytesCData, BytesEnd, BytesRef, BytesStart, BytesText};
use crate::name::QName;
#[inline]
const fn is_non_whitespace(ch: char) -> bool {
!matches!(ch, ' ' | '\r' | '\n' | '\t')
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Text<'a> {
pub(crate) text: Cow<'a, str>,
content: Range<usize>,
}
impl<'a> Text<'a> {
pub(crate) fn new(text: Cow<'a, str>) -> Self {
let start = text.find(is_non_whitespace).unwrap_or(0);
let end = text.rfind(is_non_whitespace).map_or(0, |i| i + 1);
let content = if start >= end { 0..0 } else { start..end };
Self { text, content }
}
pub fn trimmed(&self) -> Cow<'a, str> {
match self.text {
Cow::Borrowed(text) => Cow::Borrowed(&text[self.content.clone()]),
Cow::Owned(ref text) => Cow::Owned(text[self.content.clone()].to_string()),
}
}
pub fn is_blank(&self) -> bool {
self.content.is_empty()
}
}
impl<'a> Deref for Text<'a> {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
self.text.deref()
}
}
impl<'a> From<&'a str> for Text<'a> {
#[inline]
fn from(text: &'a str) -> Self {
Self::new(Cow::Borrowed(text))
}
}
impl<'a> From<String> for Text<'a> {
#[inline]
fn from(text: String) -> Self {
Self::new(Cow::Owned(text))
}
}
impl<'a> From<Cow<'a, str>> for Text<'a> {
#[inline]
fn from(text: Cow<'a, str>) -> Self {
Self::new(text)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DeEvent<'a> {
Start(BytesStart<'a>),
End(BytesEnd<'a>),
Text(Text<'a>),
Eof,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PayloadEvent<'a> {
Start(BytesStart<'a>),
End(BytesEnd<'a>),
Text(BytesText<'a>),
CData(BytesCData<'a>),
DocType(BytesText<'a>),
GeneralRef(BytesRef<'a>),
Eof,
}
impl<'a> PayloadEvent<'a> {
#[inline]
pub(crate) fn into_owned(self) -> PayloadEvent<'static> {
match self {
PayloadEvent::Start(e) => PayloadEvent::Start(e.into_owned()),
PayloadEvent::End(e) => PayloadEvent::End(e.into_owned()),
PayloadEvent::Text(e) => PayloadEvent::Text(e.into_owned()),
PayloadEvent::CData(e) => PayloadEvent::CData(e.into_owned()),
PayloadEvent::DocType(e) => PayloadEvent::DocType(e.into_owned()),
PayloadEvent::GeneralRef(e) => PayloadEvent::GeneralRef(e.into_owned()),
PayloadEvent::Eof => PayloadEvent::Eof,
}
}
}
pub(crate) struct XmlReader<'i, R: XmlRead<'i>, E: EntityResolver = PredefinedEntityResolver> {
pub(crate) reader: R,
lookahead: Result<PayloadEvent<'i>, DeError>,
entity_resolver: E,
}
impl<'i, R: XmlRead<'i>, E: EntityResolver> XmlReader<'i, R, E> {
pub(crate) fn new(mut reader: R, entity_resolver: E) -> Self {
let lookahead = reader.next();
Self {
reader,
lookahead,
entity_resolver,
}
}
pub(crate) const fn is_empty(&self) -> bool {
matches!(self.lookahead, Ok(PayloadEvent::Eof))
}
#[inline(always)]
fn next_impl(&mut self) -> Result<PayloadEvent<'i>, DeError> {
replace(&mut self.lookahead, self.reader.next())
}
#[inline(always)]
const fn current_event_is_last_text(&self) -> bool {
!matches!(
self.lookahead,
Ok(PayloadEvent::Text(_)
| PayloadEvent::CData(_)
| PayloadEvent::GeneralRef(_)
| PayloadEvent::DocType(_))
)
}
fn drain_text(&mut self, mut result: Cow<'i, str>) -> Result<DeEvent<'i>, DeError> {
loop {
if self.current_event_is_last_text() {
break;
}
match self.next_impl()? {
PayloadEvent::Text(e) => result
.to_mut()
.push_str(&e.xml_content(self.reader.xml_version())?),
PayloadEvent::CData(e) => result
.to_mut()
.push_str(&e.xml_content(self.reader.xml_version())?),
PayloadEvent::GeneralRef(e) => self.resolve_reference(result.to_mut(), e)?,
PayloadEvent::DocType(e) => {
self.entity_resolver
.capture(e)
.map_err(|err| DeError::Custom(format!("cannot parse DTD: {}", err)))?;
}
_ => unreachable!(
"Only `Text`, `CData`, `GeneralRef` or `DocType` events can come here"
),
}
}
Ok(DeEvent::Text(Text::new(result)))
}
pub(crate) fn next(&mut self) -> Result<DeEvent<'i>, DeError> {
loop {
return match self.next_impl()? {
PayloadEvent::Start(e) => Ok(DeEvent::Start(e)),
PayloadEvent::End(e) => Ok(DeEvent::End(e)),
PayloadEvent::Text(e) => self.drain_text(e.xml_content(self.reader.xml_version())?),
PayloadEvent::CData(e) => {
self.drain_text(e.xml_content(self.reader.xml_version())?)
}
PayloadEvent::DocType(e) => {
self.entity_resolver
.capture(e)
.map_err(|err| DeError::Custom(format!("cannot parse DTD: {}", err)))?;
continue;
}
PayloadEvent::GeneralRef(e) => {
let mut text = String::new();
self.resolve_reference(&mut text, e)?;
self.drain_text(text.into())
}
PayloadEvent::Eof => Ok(DeEvent::Eof),
};
}
}
fn resolve_reference(&mut self, result: &mut String, event: BytesRef) -> Result<(), DeError> {
let len = event.len();
let reference = self.decoder().decode(&event)?;
if let Some(num) = reference.strip_prefix('#') {
let codepoint = parse_number(num).map_err(EscapeError::InvalidCharRef)?;
result.push_str(codepoint.encode_utf8(&mut [0u8; 4]));
return Ok(());
}
if let Some(value) = self.entity_resolver.resolve(reference.as_ref()) {
result.push_str(value);
return Ok(());
}
Err(EscapeError::UnrecognizedEntity(0..len, reference.to_string()).into())
}
#[inline]
pub(crate) fn read_to_end(&mut self, name: QName) -> Result<(), DeError> {
match self.lookahead {
Ok(PayloadEvent::Start(ref e)) if e.name() == name => {
let result1 = self.reader.read_to_end(name);
let result2 = self.reader.read_to_end(name);
let _ = self.next_impl();
result1?;
result2?;
}
Ok(PayloadEvent::End(ref e)) if e.name() == name => {
let _ = self.next_impl();
}
Ok(_) => {
let result = self.reader.read_to_end(name);
let _ = self.next_impl();
result?;
}
Err(_) => {
self.next_impl()?;
}
}
Ok(())
}
#[inline]
pub(crate) fn decoder(&self) -> Decoder {
self.reader.decoder()
}
}