diff options
Diffstat (limited to 'src/deserialization')
| -rw-r--r-- | src/deserialization/buffer_deserializer.rs | 210 | ||||
| -rw-r--r-- | src/deserialization/mod.rs | 124 | 
2 files changed, 334 insertions, 0 deletions
diff --git a/src/deserialization/buffer_deserializer.rs b/src/deserialization/buffer_deserializer.rs new file mode 100644 index 0000000..652e1ff --- /dev/null +++ b/src/deserialization/buffer_deserializer.rs @@ -0,0 +1,210 @@ +use std::any::type_name; +use std::error::Error; +use std::io::BufRead; + +use quick_xml::events::{BytesStart, Event}; +use quick_xml::Reader; + +use crate::deserialization::{ +    Deserialize, +    Deserializer, +    DeserializerError, +    IgnoreEnd, +    WrappedDeserializeError, +}; + +macro_rules! read_event { +    ($self: ident) => {{ +        let event = if let Some(leftover_event) = $self.leftover_event.take() { +            leftover_event +        } else { +            $self.reader.read_event_into(&mut $self.buf)?.into_owned() +        }; + +        if let Event::Eof = &event { +            return Err(DeserializerError::UnexpectedEndOfFile); +        } + +        event +    }}; +} + +pub struct BufferDeserializer<Source> +{ +    reader: Reader<Source>, +    leftover_event: Option<Event<'static>>, +    buf: Vec<u8>, +} + +impl<Source> BufferDeserializer<Source> +where +    Source: BufRead, +{ +    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 BufferDeserializer<Source> +where +    Source: BufRead, +{ +    fn de_tag<De: Deserialize>( +        &mut self, +        tag_name: &str, +        ignore_end: IgnoreEnd, +    ) -> Result<De, DeserializerError> +    { +        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, DeserializerError> +    where +        Err: Error + Send + Sync + 'static, +        DeserializeFn: FnOnce(&BytesStart, &mut Self) -> Result<Output, Err>, +    { +        let deserialized = match read_event!(self) { +            Event::Start(start) if start.name().as_ref() == tag_name.as_bytes() => { +                deserialize(&start, self).map_err(|err| { +                    DeserializerError::DeserializeFailed( +                        type_name::<Output>(), +                        WrappedDeserializeError::new(err), +                    ) +                })? +            } +            event => { +                self.leftover_event = Some(event.clone().into_owned()); + +                return Err(DeserializerError::UnexpectedEvent { +                    expected_event_name: format!("start({tag_name})"), +                    found_event: event, +                }); +            } +        }; + +        if let IgnoreEnd::No = ignore_end { +            self.read_end_event(tag_name)?; +        } + +        Ok(deserialized) +    } + +    fn de_tag_list<De: Deserialize>( +        &mut self, +        tag_name: &str, +    ) -> Result<Vec<De>, DeserializerError> +    { +        let mut deserialized_items = Vec::new(); + +        loop { +            let start = match read_event!(self) { +                Event::Start(start) if start.name().as_ref() == tag_name.as_bytes() => { +                    start +                } +                Event::Comment(_) => { +                    continue; +                } +                event => { +                    self.leftover_event = Some(event.into_owned()); +                    break; +                } +            }; + +            let deserialized = De::deserialize(&start, self).map_err(|err| { +                DeserializerError::DeserializeFailed( +                    type_name::<De>(), +                    WrappedDeserializeError::new(err), +                ) +            })?; + +            self.read_end_event(tag_name)?; + +            deserialized_items.push(deserialized); +        } + +        Ok(deserialized_items) +    } + +    fn de_text(&mut self) -> Result<String, DeserializerError> +    { +        let text = match read_event!(self) { +            Event::Text(text) => Ok(text), +            event => { +                self.leftover_event = Some(event.clone().into_owned()); + +                Err(DeserializerError::UnexpectedEvent { +                    expected_event_name: "text".to_string(), +                    found_event: event, +                }) +            } +        }? +        .unescape()?; + +        Ok(text.to_string()) +    } + +    fn skip_to_tag_start(&mut self, tag_name: &str) -> Result<(), DeserializerError> +    { +        loop { +            match read_event!(self) { +                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<(), DeserializerError> +    { +        loop { +            match read_event!(self) { +                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> BufferDeserializer<Source> +where +    Source: BufRead, +{ +    fn read_end_event(&mut self, tag_name: &str) -> Result<(), DeserializerError> +    { +        let event = read_event!(self); + +        if matches!(&event, Event::End(end) if end.name().as_ref() == tag_name.as_bytes()) +        { +            return Ok(()); +        } + +        Err(DeserializerError::UnexpectedEvent { +            expected_event_name: "end".to_string(), +            found_event: event.into_owned(), +        }) +    } +} diff --git a/src/deserialization/mod.rs b/src/deserialization/mod.rs new file mode 100644 index 0000000..fa25e4b --- /dev/null +++ b/src/deserialization/mod.rs @@ -0,0 +1,124 @@ +use std::error::Error; +use std::ops::Deref; + +use quick_xml::events::{BytesStart, Event}; + +pub mod buffer_deserializer; + +pub trait Deserialize: Sized +{ +    type Error: Error + Send + Sync + 'static; + +    fn deserialize<TDeserializer: Deserializer>( +        start: &BytesStart, +        deserializer: &mut TDeserializer, +    ) -> Result<Self, Self::Error>; +} + +pub trait Deserializer +{ +    fn de_tag<De: Deserialize>( +        &mut self, +        tag_name: &str, +        ignore_end: IgnoreEnd, +    ) -> Result<De, DeserializerError>; + +    fn de_tag_with<Output, Err, DeserializeFn>( +        &mut self, +        tag_name: &str, +        ignore_end: IgnoreEnd, +        deserialize: DeserializeFn, +    ) -> Result<Output, DeserializerError> +    where +        Err: Error + Send + Sync + 'static, +        DeserializeFn: FnOnce(&BytesStart, &mut Self) -> Result<Output, Err>; + +    fn de_tag_list<De: Deserialize>( +        &mut self, +        tag_name: &str, +    ) -> Result<Vec<De>, DeserializerError>; + +    fn de_text(&mut self) -> Result<String, DeserializerError>; + +    fn skip_to_tag_start(&mut self, tag_name: &str) -> Result<(), DeserializerError>; + +    fn skip_to_tag_end(&mut self, tag_name: &str) -> Result<(), DeserializerError>; +} + +pub enum IgnoreEnd +{ +    Yes, +    No, +} + +/// Function pointer type passable to [`Deserializer::de_tag_with`]. +pub type DeserializeWithFn<Output, Err, Deserializer> = +    fn(&BytesStart, &mut Deserializer) -> Result<Output, Err>; + +#[derive(Debug, thiserror::Error)] +pub enum DeserializerError +{ +    #[error("Failed to read")] +    ReadFailed(#[from] quick_xml::Error), + +    #[error("Failed to deserialize {0}")] +    DeserializeFailed(&'static str, #[source] WrappedDeserializeError), + +    #[error("Expected {expected_event_name} event. Found {found_event:?}")] +    UnexpectedEvent +    { +        expected_event_name: String, +        found_event: Event<'static>, +    }, + +    #[error("Unexpected end of file")] +    UnexpectedEndOfFile, +} + +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct WrappedDeserializeError(Box<dyn Error + Send + Sync>); + +impl WrappedDeserializeError +{ +    fn new<Err: Error + Send + Sync + 'static>(err: Err) -> Self +    { +        Self(Box::new(err)) +    } +} + +impl Deref for WrappedDeserializeError +{ +    type Target = dyn Error; + +    fn deref(&self) -> &Self::Target +    { +        self.0.as_ref() +    } +} + +pub trait ResultExt<Value> +{ +    fn try_event(self) -> Result<Option<Value>, DeserializerError>; +} + +impl<Value> ResultExt<Value> for Result<Value, DeserializerError> +{ +    fn try_event(self) -> Result<Option<Value>, DeserializerError> +    { +        self.map_or_else( +            |err| { +                if let DeserializerError::UnexpectedEvent { +                    expected_event_name: _, +                    found_event: _, +                } = err +                { +                    return Ok(None); +                } + +                Err(err) +            }, +            |value| Ok(Some(value)), +        ) +    } +}  | 
