//! Deserializer. use std::convert::Infallible; use crate::tagged::TagStart; use crate::util::{feature_alternate, trait_alias}; 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: Func, ) -> Result> where Output: MaybeStatic, Err: std::error::Error + Send + Sync + 'static, Func: FnOnce(&TagStart, &mut Self) -> Result + MaybeStatic; /// Deserializes a list of tagged elements. /// /// # Errors /// Returns `Err` if deserialization fails. fn de_tag_list( &mut self, tag_name: Option, ) -> Result, Error> where De: DeserializeTagged, TagName: AsRef + MaybeStatic; /// 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>; } trait_alias!( bounds_when_feature = "deserializer-static-generics", /// Bound to `'static` if the `deserializer-static-generics` feature is enabled. pub MaybeStatic: 'static; ); /// Whether or not to skip the end tag of a tagged element. /// /// **Should be `No`**. #[derive(Debug, Default, PartialEq, Eq)] 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, } } } impl Error { /// Converts `Self` into `Err`. pub fn into_error(self) -> Err where Err: From + From>, { if let Error::DeserializeFailed(de_err) = self { return de_err.into(); } self.into_never_de_err().into() } } /// XML error. #[derive(Debug, thiserror::Error)] #[error(transparent)] pub struct XMLError(#[from] quick_xml::Error); /// Implements conversion from [`Error`] with [`From`] for the given error type. /// /// Allows for custom error types with source error types to easily be converted into with /// `?`. /// /// The given error type should implement `From>`. /// /// # Examples /// ``` /// use std::convert::Infallible; /// /// use xml_stinks::deserializer::Error as DeserializerError; /// use xml_stinks::impl_from_deserializer_error; /// /// #[derive(Debug, thiserror::Error)] /// enum FooError /// { /// #[error("Deserialization failed")] /// DeserializeFailed(#[from] DeserializerError), /// /// #[error("Invalid bar")] /// InvalidBar(#[from] BarError), /// } /// /// impl_from_deserializer_error!(FooError); /// /// #[derive(Debug, thiserror::Error)] /// enum BarError /// { /// #[error("Oops")] /// Oops, /// } /// /// let err_a: FooError = DeserializerError::::UnexpectedEndOfFile.into(); /// /// assert!(matches!( /// err_a, /// FooError::DeserializeFailed(DeserializerError::UnexpectedEndOfFile) /// )); /// /// let err_b: FooError = DeserializerError::DeserializeFailed(BarError::Oops).into(); /// /// assert!(matches!(err_b, FooError::InvalidBar(BarError::Oops))); /// ``` #[macro_export] macro_rules! impl_from_deserializer_error { ($err: path) => { impl> From<::xml_stinks::deserializer::Error> for $err { fn from(err: ::xml_stinks::deserializer::Error) -> Self { if let ::xml_stinks::deserializer::Error::DeserializeFailed(de_err) = err { return de_err.into(); } err.into_never_de_err().into() } } }; } feature_alternate!( feature = "deserializer-static-generics", /// Conditional compilation based on whether or not the `deserializer-static-generics` /// feature is enabled. /// /// # Examples /// ``` /// use std::io::Cursor; /// /// use xml_stinks::xml_stinks_if_deserializer_static_generics; /// use xml_stinks::deserializer::buffered::Buffered as BufferedDeserializer; /// use xml_stinks::deserializer::Deserializer; /// /// fn do_something(bytes: &[u8]) /// { /// let deserializer = xml_stinks_if_deserializer_static_generics!(then { /// BufferedDeserializer::new(Cursor::new(bytes.to_vec())); /// } else { /// // This wouldn't compile if the deserializer-static-generics feature was /// // enabled /// BufferedDeserializer::new(bytes); /// }); /// /// // ... /// } /// ``` when_enabled = #[macro_export] macro_rules! xml_stinks_if_deserializer_static_generics { (then { $($then: tt)* }$(else { $($else: tt)* })?) => { $($then)* }; }, when_disabled = #[macro_export] macro_rules! xml_stinks_if_deserializer_static_generics { (then { $($then: tt)* }$(else { $($else: tt)* })?) => { $($($else)*)? }; } );