From e762babd9e69400ccd178ba8946168640093eb63 Mon Sep 17 00:00:00 2001 From: HampusM Date: Sat, 15 Apr 2023 18:26:29 +0200 Subject: feat: add deserialization --- src/deserializer/buffered.rs | 212 +++++++++++++++++++++++++++++++++++++++++++ src/deserializer/mod.rs | 179 ++++++++++++++++++++++++++++++++++++ 2 files changed, 391 insertions(+) create mode 100644 src/deserializer/buffered.rs create mode 100644 src/deserializer/mod.rs (limited to 'src/deserializer') 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 +{ + reader: Reader, + leftover_event: Option>, + buf: Vec, +} + +impl Buffered +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 Deserializer for Buffered +where + Source: BufRead, +{ + fn de_tag( + &mut self, + tag_name: &str, + ignore_end: IgnoreEnd, + ) -> Result> + { + self.de_tag_with(tag_name, ignore_end, De::deserialize) + } + + fn de_tag_with( + &mut self, + tag_name: &str, + ignore_end: IgnoreEnd, + deserialize: DeserializeFn, + ) -> Result> + where + Err: std::error::Error + Send + Sync + 'static, + DeserializeFn: FnOnce(&TagStart, &mut Self) -> Result, + { + 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( + &mut self, + tag_name: Option<&str>, + ) -> Result, 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> + { + let text = match self.read_event::()? { + Event::Text(text) => Ok(text), + event => { + self.leftover_event = Some(event.clone().into_owned()); + + Err(Error::::UnexpectedEvent { + expected_event_name: "text".to_string(), + found_event: event.describe().unwrap(), + }) + } + }? + .unescape() + .map_err(|err| Error::::XMLError(err.into()))?; + + Ok(text.to_string()) + } + + fn skip_to_tag_start(&mut self, tag_name: &str) -> Result<(), Error> + { + loop { + match self.read_event::()? { + 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> + { + loop { + match self.read_event::()? { + Event::End(end) if end.name().as_ref() == tag_name.as_bytes() => { + self.leftover_event = Some(Event::End(end).into_owned()); + + return Ok(()); + } + _ => {} + } + } + } +} + +impl Buffered +where + Source: BufRead, +{ + fn read_end_event(&mut self, tag_name: &[u8]) -> Result<(), Error> + { + let event = self.read_event::()?; + + 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(&mut self) -> Result, Error> + { + 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::::XMLError(err.into()))? + .into_owned() + }; + + if let Event::Eof = &event { + return Err(Error::UnexpectedEndOfFile); + } + + Ok(event) + } +} diff --git a/src/deserializer/mod.rs b/src/deserializer/mod.rs new file mode 100644 index 0000000..bd0c0e4 --- /dev/null +++ b/src/deserializer/mod.rs @@ -0,0 +1,179 @@ +//! Deserializer. +use std::convert::Infallible; + +use crate::tagged::TagStart; +use crate::DeserializeTagged; + +pub mod buffered; + +/// XML deserializer. +pub trait Deserializer +{ + /// Deserializes a tagged element. + /// + /// # Errors + /// Returns `Err` if deserialization fails. + fn de_tag( + &mut self, + tag_name: &str, + ignore_end: IgnoreEnd, + ) -> Result>; + + /// Deserializes a tagged element using the given function. + /// + /// # Errors + /// Returns `Err` if deserialization fails. + fn de_tag_with( + &mut self, + tag_name: &str, + ignore_end: IgnoreEnd, + deserialize: DeserializeFn, + ) -> Result> + where + Err: std::error::Error + Send + Sync + 'static, + DeserializeFn: FnOnce(&TagStart, &mut Self) -> Result; + + /// Deserializes a list of tagged elements. + /// + /// # Errors + /// Returns `Err` if deserialization fails. + fn de_tag_list( + &mut self, + tag_name: Option<&str>, + ) -> Result, Error>; + + /// Deserializes a text element. + /// + /// # Errors + /// Returns `Err` if deserialization fails. + fn de_text(&mut self) -> Result>; + + /// Skips past all elements until a tagged element with the name `tag_name` is + /// reached. + /// + /// # Errors + /// Returns `Err` if unsuccessful. + fn skip_to_tag_start(&mut self, tag_name: &str) -> Result<(), Error>; + + /// Skips past all elements until the end of a tagged element with the name `tag_name` + /// is reached. + /// + /// # Errors + /// Returns `Err` if unsuccessful. + fn skip_to_tag_end(&mut self, tag_name: &str) -> Result<(), Error>; +} + +/// Whether or not to skip the end tag of a tagged element. +/// +/// **Should be `No`**. +#[derive(Debug, Default)] +pub enum IgnoreEnd +{ + /// Skip the end tag. + /// + /// **Will cause problems in most cases and should be used very carefully**. + Yes, + + /// Don't skip the end tag. + #[default] + No, +} + +/// [`Deserializer`] error. +#[derive(Debug, thiserror::Error)] +#[non_exhaustive] +pub enum Error +{ + /// A XML error occurred. + #[error("A XML error occurred")] + XMLError(#[source] XMLError), + + /// Failed to deserialize. + #[error("Failed to deserialize")] + DeserializeFailed(#[from] DeError), + + /// Unexpected event. + #[error("Expected {expected_event_name} event. Found {found_event}")] + UnexpectedEvent + { + /// The name of the expected event. + expected_event_name: String, + + /// The found event. + found_event: String, + }, + + /// Unexpected end of file. + #[error("Unexpected end of file")] + UnexpectedEndOfFile, +} + +impl Error +{ + /// Returns `Self` with `DeError` as [`Infallible`]. + /// + /// # Panics + /// Will panic if `Self` is the `DeserializeFailed` variant. + pub fn into_never_de_err(self) -> Error + { + match self { + Self::XMLError(xml_err) => Error::XMLError(xml_err), + Self::DeserializeFailed(_) => { + panic!("is a deserialization error"); + } + Self::UnexpectedEvent { + expected_event_name, + found_event, + } => Error::UnexpectedEvent { + expected_event_name, + found_event, + }, + Self::UnexpectedEndOfFile => Error::UnexpectedEndOfFile, + } + } +} + +impl Error +{ + fn into_with_de_error(self) -> Error + { + match self { + Self::XMLError(xml_err) => Error::XMLError(xml_err), + Self::DeserializeFailed(_) => { + unreachable!(); + } + Self::UnexpectedEvent { + expected_event_name, + found_event, + } => Error::UnexpectedEvent { + expected_event_name, + found_event, + }, + Self::UnexpectedEndOfFile => Error::UnexpectedEndOfFile, + } + } +} + +impl From>> for Error +{ + fn from(err: Error>) -> Self + { + match err { + Error::XMLError(xml_err) => Self::XMLError(xml_err), + Error::DeserializeFailed(de_err) => de_err, + Error::UnexpectedEvent { + expected_event_name, + found_event, + } => Self::UnexpectedEvent { + expected_event_name, + found_event, + }, + Error::UnexpectedEndOfFile => Self::UnexpectedEndOfFile, + } + } +} + +/// XML error. +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct XMLError(#[from] quick_xml::Error); -- cgit v1.2.3-18-g5258