use beef::lean::Cow;
use quick_xml::events::BytesDecl;
use quick_xml::events::BytesEnd;
use quick_xml::events::BytesStart;
use quick_xml::events::Event;
use quick_xml::name::QName;
use quick_xml::Reader;
use crate::UnexpectedTokenSnafu;
use crate::XmlError;
use crate::XmlResult;
pub(crate) struct XmlReader<'a> {
inner: Reader<&'a [u8]>,
peeked: Option<Event<'a>>,
}
impl<'a> XmlReader<'a> {
pub(crate) fn from_str(document: &'a str) -> Self {
let mut inner = quick_xml::Reader::from_str(document);
inner.expand_empty_elements(true).trim_text(true);
Self {
inner,
peeked: None,
}
}
}
impl<'a> XmlReader<'a> {
pub(crate) fn expect_boolean(&mut self) -> XmlResult<bool> {
let event = self.read_event()?;
let Event::Text(text) = event else { todo!() };
match &*text.unescape()? {
"true" => Ok(true),
"false" => Ok(false),
_ => todo!(),
}
}
pub(crate) fn expect_element_end(
&mut self,
start: BytesStart,
) -> Result<BytesEnd<'a>, XmlError> {
match self.read_event()? {
Event::End(end) if end.name() == start.name() => Ok(end),
received => {
let received = received.into_owned();
UnexpectedTokenSnafu {
expected: format!("element end for {start:?}"),
received,
}
.fail()
}
}
}
pub(crate) fn expect_element_start(&mut self, tag: &str) -> Result<BytesStart<'a>, XmlError> {
match self.read_event()? {
Event::Start(start) if start.name().into_inner() == tag.as_bytes() => Ok(start),
received => {
let received = received.into_owned();
UnexpectedTokenSnafu {
expected: format!("element start for {tag:?}"),
received,
}
.fail()
}
}
}
pub(crate) fn expect_start_tag(&mut self) -> Result<BytesStart<'a>, XmlError> {
match self.read_event()? {
Event::Start(start) => Ok(start),
received => {
let received = received.into_owned();
UnexpectedTokenSnafu {
expected: "start tag",
received,
}
.fail()
}
}
}
pub(crate) fn expect_text(&mut self) -> XmlResult<Cow<'a, str>> {
let event = self.read_event()?;
let Event::Text(text) = event else { todo!() };
text.unescape().map(Cow::from).map_err(XmlError::from)
}
pub(crate) fn parse_element<T, F>(&mut self, tag: &str, body: F) -> XmlResult<T>
where
F: FnOnce(&mut Self, &BytesStart<'a>) -> XmlResult<T>,
{
let start = self.expect_element_start(tag)?;
let element = body(self, &start)?;
self.expect_element_end(start)?;
Ok(element)
}
pub(crate) fn parse_list<T, F>(&mut self, tag: &str, mut body: F) -> XmlResult<Vec<T>>
where
F: FnMut(&mut Self, &BytesStart<'a>) -> XmlResult<T>,
{
let mut result = Vec::new();
loop {
let Event::Start(start) = self.peek_event()? else {
break;
};
if start.name().as_ref() != tag.as_bytes() {
break;
}
let Ok(Event::Start(start)) = self.read_event() else {
unreachable!()
};
let element = body(self, &start)?;
self.expect_element_end(start)?;
result.push(element);
}
Ok(result)
}
pub(crate) fn parse_optional_element<T, F>(
&mut self,
tag: &str,
parser: F,
) -> XmlResult<Option<T>>
where
F: FnOnce(&mut Self, &BytesStart<'a>) -> XmlResult<T>,
{
let Event::Start(start) = self.peek_event()? else {
return Ok(None);
};
if start.name().as_ref() != tag.as_bytes() {
return Ok(None);
}
let Ok(Event::Start(start)) = self.read_event() else {
unreachable!()
};
let element = parser(self, &start)?;
self.expect_element_end(start)?;
Ok(Some(element))
}
pub(crate) fn peek_event(&mut self) -> XmlResult<&Event<'a>> {
let peeked = if let Some(ref peeked) = self.peeked {
peeked
} else {
let event = self.inner.read_event()?;
let peeked = self.peeked.insert(event);
peeked
};
Ok(peeked)
}
pub(crate) fn peek_start_name(&mut self) -> XmlResult<QName> {
let Event::Start(start) = self.peek_event()? else {
todo!();
};
Ok(start.name())
}
pub(crate) fn read_event(&mut self) -> XmlResult<Event<'a>> {
if let Some(event) = self.peeked.take() {
Ok(event)
} else {
self.inner.read_event().map_err(XmlError::from)
}
}
pub(crate) fn try_declaration(&mut self) -> XmlResult<Option<BytesDecl>> {
let Event::Decl(_) = self.peek_event()? else {
return Ok(None);
};
let Ok(Event::Decl(decl)) = self.read_event() else {
unreachable!()
};
Ok(Some(decl))
}
}
impl<'a> XmlReader<'a> {
pub fn inner(&self) -> &Reader<&'a [u8]> {
&self.inner
}
}