aboutsummaryrefslogtreecommitdiff
path: root/src/deserializer/buffered.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/deserializer/buffered.rs')
-rw-r--r--src/deserializer/buffered.rs212
1 files changed, 212 insertions, 0 deletions
diff --git a/src/deserializer/buffered.rs b/src/deserializer/buffered.rs
new file mode 100644
index 0000000..7a6058b
--- /dev/null
+++ b/src/deserializer/buffered.rs
@@ -0,0 +1,212 @@
+//! Buffered XML deserializer.
+use std::convert::Infallible;
+use std::io::BufRead;
+
+use quick_xml::events::Event;
+use quick_xml::Reader;
+
+use crate::deserializer::{Deserializer, Error, IgnoreEnd};
+use crate::event::EventExt;
+use crate::tagged::TagStart;
+use crate::DeserializeTagged;
+
+/// XML deserializer using a source which has an internal buffer.
+pub struct Buffered<Source: BufRead>
+{
+ reader: Reader<Source>,
+ leftover_event: Option<Event<'static>>,
+ buf: Vec<u8>,
+}
+
+impl<Source> Buffered<Source>
+where
+ Source: BufRead,
+{
+ /// Returns a new [`Buffered`].
+ pub fn new(source: Source) -> 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<Source> Deserializer for Buffered<Source>
+where
+ Source: BufRead,
+{
+ 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>(
+ &mut self,
+ tag_name: Option<&str>,
+ ) -> 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.map_or_else(
+ || true,
+ |expected_tag_name| {
+ start.name().as_ref() == expected_tag_name.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())
+ .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<Source> Buffered<Source>
+where
+ Source: BufRead,
+{
+ 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)
+ }
+}