use core::fmt;
use std::borrow::Cow;
use quick_xml::{events::Event, reader::Reader};
use crate::Error;
mod sealed {
pub trait Sealed {}
impl Sealed for quick_xml::Reader<&[u8]> {}
}
use self::sealed::Sealed;
pub trait Expect<'a>: Sealed {
fn expect_element(&mut self, name: &str) -> Result<ElementReader<'a, '_>, Error>;
fn expect_empty(&mut self, name: &str) -> Result<(), Error>;
fn expect_eof(&mut self) -> Result<(), Error>;
fn expect_text(&mut self) -> Result<Cow<'a, str>, Error>;
}
impl<'a> Expect<'a> for Reader<&'a [u8]> {
fn expect_element(&mut self, name: &str) -> Result<ElementReader<'a, '_>, Error> {
log::debug!("expecting element <{name}>");
match self.read_event()? {
Event::Start(tag) if tag.name().as_ref() == name.as_bytes() => {
log::debug!("found element <{name}>, scanning for end tag");
let end = tag.to_end();
let span = self.read_text(end.name())?;
log::debug!("found matching end tag, decoding contents");
log::trace!("got contents {span}");
Ok(ElementReader {
_parent: self,
span,
})
}
Event::Eof => Err(Error::Eof),
event => Err(Error::unexpected_event(event)),
}
}
fn expect_empty(&mut self, name: &str) -> Result<(), Error> {
log::debug!("expecting element <{name}/>");
match self.read_event()? {
Event::Empty(tag) if tag.name().as_ref() == name.as_bytes() => Ok(()),
Event::Eof => Err(Error::Eof),
event => Err(Error::unexpected_event(event)),
}
}
fn expect_eof(&mut self) -> Result<(), Error> {
log::debug!("expecting end-of-file");
match self.read_event()? {
Event::Eof => Ok(()),
event => Err(Error::unexpected_event(event)),
}
}
fn expect_text(&mut self) -> Result<Cow<'a, str>, Error> {
log::debug!("expecting text node");
match self.read_event()? {
Event::Text(txt) => Ok(txt.unescape()?),
Event::Eof => Err(Error::Eof),
event => Err(Error::unexpected_event(event)),
}
}
}
pub struct ElementReader<'a, 'b> {
_parent: &'b mut Reader<&'a [u8]>,
span: Cow<'a, str>,
}
impl ElementReader<'_, '_> {
pub fn read_inner<F, T>(self, mut f: F) -> Result<T, Error>
where
F: FnMut(
&mut Reader<&[u8]>,
) -> Result<T, Box<dyn std::error::Error + Send + Sync + 'static>>,
{
let slice = self.span.as_ref();
let mut reader = Reader::from_str(slice);
_ = reader.trim_text(true);
let result = f(&mut reader)?;
reader.expect_eof()?;
Ok(result)
}
}
impl fmt::Debug for ElementReader<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ElementReader")
.field("span", &self.span)
.finish()
}
}