diff options
| author | HampusM <hampus@hampusmat.com> | 2023-04-15 18:26:29 +0200 | 
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2023-05-09 19:51:02 +0200 | 
| commit | e762babd9e69400ccd178ba8946168640093eb63 (patch) | |
| tree | e07a56940f0c4a3551c87afad80bb949969335c7 /src/deserializer | |
| parent | da509c366972ac6d423f95732cd3d319a2265841 (diff) | |
feat: add deserialization
Diffstat (limited to 'src/deserializer')
| -rw-r--r-- | src/deserializer/buffered.rs | 212 | ||||
| -rw-r--r-- | src/deserializer/mod.rs | 179 | 
2 files changed, 391 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) +    } +} 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<De: DeserializeTagged>( +        &mut self, +        tag_name: &str, +        ignore_end: IgnoreEnd, +    ) -> Result<De, Error<De::Error>>; + +    /// Deserializes a tagged element using the given function. +    /// +    /// # Errors +    /// Returns `Err` if deserialization fails. +    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>; + +    /// Deserializes a list of tagged elements. +    /// +    /// # Errors +    /// Returns `Err` if deserialization fails. +    fn de_tag_list<De: DeserializeTagged>( +        &mut self, +        tag_name: Option<&str>, +    ) -> Result<Vec<De>, Error<De::Error>>; + +    /// Deserializes a text element. +    /// +    /// # Errors +    /// Returns `Err` if deserialization fails. +    fn de_text(&mut self) -> Result<String, Error<Infallible>>; + +    /// 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<Infallible>>; + +    /// 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<Infallible>>; +} + +/// 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<DeError> +{ +    /// 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<DeError> Error<DeError> +{ +    /// Returns `Self` with `DeError` as [`Infallible`]. +    /// +    /// # Panics +    /// Will panic if `Self` is the `DeserializeFailed` variant. +    pub fn into_never_de_err(self) -> Error<Infallible> +    { +        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<Infallible> +{ +    fn into_with_de_error<DeError>(self) -> Error<DeError> +    { +        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<Error<Error<Infallible>>> for Error<Infallible> +{ +    fn from(err: Error<Error<Infallible>>) -> 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);  | 
