use std::convert::Infallible;
use std::io::BufRead;
use quick_xml::events::Event;
use quick_xml::Reader;
use crate::deserializer::{Deserializer, Error, IgnoreEnd, MaybeStatic};
use crate::event::EventExt;
use crate::tagged::TagStart;
use crate::util::trait_alias;
use crate::DeserializeTagged;
trait_alias! {
pub Source: BufRead + MaybeStatic;
}
pub struct Buffered<TSource: Source>
{
reader: Reader<TSource>,
leftover_event: Option<Event<'static>>,
buf: Vec<u8>,
}
impl<TSource: Source> Buffered<TSource>
{
pub fn new(source: TSource) -> Self
{
let mut reader = Reader::from_reader(source);
reader.trim_text(true);
reader.expand_empty_elements(true);
Self {
reader,
leftover_event: None,
buf: Vec::new(),
}
}
}
impl<TSource: Source> Deserializer for Buffered<TSource>
{
fn de_tag<De: DeserializeTagged>(
&mut self,
tag_name: &str,
ignore_end: IgnoreEnd,
) -> Result<De, Error<De::Error>>
{
self.de_tag_with(tag_name, ignore_end, De::deserialize)
}
fn de_tag_with<Output, Err, DeserializeFn>(
&mut self,
tag_name: &str,
ignore_end: IgnoreEnd,
deserialize: DeserializeFn,
) -> Result<Output, Error<Err>>
where
Err: std::error::Error + Send + Sync + 'static,
DeserializeFn: FnOnce(&TagStart, &mut Self) -> Result<Output, Err>,
{
let deserialized = match self.read_event()? {
Event::Start(start) if start.name().as_ref() == tag_name.as_bytes() => {
deserialize(&TagStart::from_inner(start), self)
.map_err(Error::DeserializeFailed)?
}
event => {
self.leftover_event = Some(event.clone().into_owned());
return Err(Error::UnexpectedEvent {
expected_event_name: format!("start({tag_name})"),
found_event: event.describe().unwrap(),
});
}
};
if let IgnoreEnd::No = ignore_end {
self.read_end_event(tag_name.as_bytes())
.map_err(Error::into_with_de_error)?;
}
Ok(deserialized)
}
fn de_tag_list<De: DeserializeTagged, Name: AsRef<str>>(
&mut self,
tag_name: Option<Name>,
) -> Result<Vec<De>, Error<De::Error>>
{
let mut deserialized_items = Vec::new();
loop {
let start = match self.read_event()? {
Event::Start(start)
if tag_name.as_ref().map_or_else(
|| true,
|expected_tag_name| {
start.name().as_ref() == expected_tag_name.as_ref().as_bytes()
},
) =>
{
TagStart::from_inner(start)
}
Event::Comment(_) => {
continue;
}
event => {
self.leftover_event = Some(event.into_owned());
break;
}
};
let deserialized =
De::deserialize(&start, self).map_err(Error::DeserializeFailed)?;
self.read_end_event(start.name_bytes())
.map_err(Error::into_with_de_error)?;
deserialized_items.push(deserialized);
}
Ok(deserialized_items)
}
fn de_text(&mut self) -> Result<String, Error<Infallible>>
{
let text = match self.read_event::<Infallible>()? {
Event::Text(text) => Ok(text),
event => {
self.leftover_event = Some(event.clone().into_owned());
Err(Error::<Infallible>::UnexpectedEvent {
expected_event_name: "text".to_string(),
found_event: event.describe().unwrap(),
})
}
}?
.unescape()
.map_err(|err| Error::<Infallible>::XMLError(err.into()))?;
Ok(text.to_string())
}
fn skip_to_tag_start(&mut self, tag_name: &str) -> Result<(), Error<Infallible>>
{
loop {
match self.read_event::<Infallible>()? {
Event::Start(start) if start.name().as_ref() == tag_name.as_bytes() => {
self.leftover_event = Some(Event::Start(start).into_owned());
break;
}
_ => {}
}
}
Ok(())
}
fn skip_to_tag_end(&mut self, tag_name: &str) -> Result<(), Error<Infallible>>
{
loop {
match self.read_event::<Infallible>()? {
Event::End(end) if end.name().as_ref() == tag_name.as_bytes() => {
self.leftover_event = Some(Event::End(end).into_owned());
return Ok(());
}
_ => {}
}
}
}
}
impl<TSource: Source> Buffered<TSource>
{
fn read_end_event(&mut self, tag_name: &[u8]) -> Result<(), Error<Infallible>>
{
let event = self.read_event::<Infallible>()?;
if matches!(&event, Event::End(end) if end.name().as_ref() == tag_name) {
return Ok(());
}
Err(Error::UnexpectedEvent {
expected_event_name: "end".to_string(),
found_event: event.describe().unwrap(),
})
}
fn read_event<DeError>(&mut self) -> Result<Event<'static>, Error<DeError>>
{
let event = if let Some(leftover_event) = self.leftover_event.take() {
leftover_event
} else {
self.reader
.read_event_into(&mut self.buf)
.map_err(|err| Error::<DeError>::XMLError(err.into()))?
.into_owned()
};
if let Event::Eof = &event {
return Err(Error::UnexpectedEndOfFile);
}
Ok(event)
}
}