xml_stinks/deserializer/
mod.rs

1//! Deserializer.
2use std::convert::Infallible;
3
4use crate::tagged::TagStart;
5use crate::util::{feature_alternate, trait_alias};
6use crate::DeserializeTagged;
7
8pub mod buffered;
9
10/// XML deserializer.
11pub trait Deserializer
12{
13    /// Deserializes a tagged element.
14    ///
15    /// # Errors
16    /// Returns `Err` if deserialization fails.
17    fn de_tag<De: DeserializeTagged>(
18        &mut self,
19        tag_name: &str,
20        ignore_end: IgnoreEnd,
21    ) -> Result<De, Error<De::Error>>;
22
23    /// Deserializes a tagged element using the given function.
24    ///
25    /// # Errors
26    /// Returns `Err` if deserialization fails.
27    fn de_tag_with<Output, Err, Func>(
28        &mut self,
29        tag_name: &str,
30        ignore_end: IgnoreEnd,
31        deserialize: Func,
32    ) -> Result<Output, Error<Err>>
33    where
34        Output: MaybeStatic,
35        Err: std::error::Error + Send + Sync + 'static,
36        Func: FnOnce(&TagStart, &mut Self) -> Result<Output, Err> + MaybeStatic;
37
38    /// Deserializes a list of tagged elements.
39    ///
40    /// # Errors
41    /// Returns `Err` if deserialization fails.
42    fn de_tag_list<De, TagName>(
43        &mut self,
44        tag_name: Option<TagName>,
45    ) -> Result<Vec<De>, Error<De::Error>>
46    where
47        De: DeserializeTagged,
48        TagName: AsRef<str> + MaybeStatic;
49
50    /// Deserializes a text element.
51    ///
52    /// # Errors
53    /// Returns `Err` if deserialization fails.
54    fn de_text(&mut self) -> Result<String, Error<Infallible>>;
55
56    /// Skips past all elements until a tagged element with the name `tag_name` is
57    /// reached.
58    ///
59    /// # Errors
60    /// Returns `Err` if unsuccessful.
61    fn skip_to_tag_start(&mut self, tag_name: &str) -> Result<(), Error<Infallible>>;
62
63    /// Skips past all elements until the end of a tagged element with the name `tag_name`
64    /// is reached.
65    ///
66    /// # Errors
67    /// Returns `Err` if unsuccessful.
68    fn skip_to_tag_end(&mut self, tag_name: &str) -> Result<(), Error<Infallible>>;
69}
70
71trait_alias!(
72    bounds_when_feature = "deserializer-static-generics",
73    /// Bound to `'static` if the `deserializer-static-generics` feature is enabled.
74    pub MaybeStatic: 'static;
75);
76
77/// Whether or not to skip the end tag of a tagged element.
78///
79/// **Should be `No`**.
80#[derive(Debug, Default, PartialEq, Eq)]
81pub enum IgnoreEnd
82{
83    /// Skip the end tag.
84    ///
85    /// **Will cause problems in most cases and should be used very carefully**.
86    Yes,
87
88    /// Don't skip the end tag.
89    #[default]
90    No,
91}
92
93/// [`Deserializer`] error.
94#[derive(Debug, thiserror::Error)]
95#[non_exhaustive]
96pub enum Error<DeError>
97{
98    /// A XML error occurred.
99    #[error("A XML error occurred")]
100    XMLError(#[source] XMLError),
101
102    /// Failed to deserialize.
103    #[error("Failed to deserialize")]
104    DeserializeFailed(#[from] DeError),
105
106    /// Unexpected event.
107    #[error("Expected {expected_event_name} event. Found {found_event}")]
108    UnexpectedEvent
109    {
110        /// The name of the expected event.
111        expected_event_name: String,
112
113        /// The found event.
114        found_event: String,
115    },
116
117    /// Unexpected end of file.
118    #[error("Unexpected end of file")]
119    UnexpectedEndOfFile,
120}
121
122impl<DeError> Error<DeError>
123{
124    /// Returns `Self` with `DeError` as [`Infallible`].
125    ///
126    /// # Panics
127    /// Will panic if `Self` is the `DeserializeFailed` variant.
128    pub fn into_never_de_err(self) -> Error<Infallible>
129    {
130        match self {
131            Self::XMLError(xml_err) => Error::XMLError(xml_err),
132            Self::DeserializeFailed(_) => {
133                panic!("is a deserialization error");
134            }
135            Self::UnexpectedEvent {
136                expected_event_name,
137                found_event,
138            } => Error::UnexpectedEvent {
139                expected_event_name,
140                found_event,
141            },
142            Self::UnexpectedEndOfFile => Error::UnexpectedEndOfFile,
143        }
144    }
145}
146
147impl Error<Infallible>
148{
149    fn into_with_de_error<DeError>(self) -> Error<DeError>
150    {
151        match self {
152            Self::XMLError(xml_err) => Error::XMLError(xml_err),
153            Self::DeserializeFailed(_) => {
154                unreachable!();
155            }
156            Self::UnexpectedEvent {
157                expected_event_name,
158                found_event,
159            } => Error::UnexpectedEvent {
160                expected_event_name,
161                found_event,
162            },
163            Self::UnexpectedEndOfFile => Error::UnexpectedEndOfFile,
164        }
165    }
166}
167
168impl From<Error<Error<Infallible>>> for Error<Infallible>
169{
170    fn from(err: Error<Error<Infallible>>) -> Self
171    {
172        match err {
173            Error::XMLError(xml_err) => Self::XMLError(xml_err),
174            Error::DeserializeFailed(de_err) => de_err,
175            Error::UnexpectedEvent {
176                expected_event_name,
177                found_event,
178            } => Self::UnexpectedEvent {
179                expected_event_name,
180                found_event,
181            },
182            Error::UnexpectedEndOfFile => Self::UnexpectedEndOfFile,
183        }
184    }
185}
186
187impl<DeError> Error<DeError>
188{
189    /// Converts `Self` into `Err`.
190    pub fn into_error<Err>(self) -> Err
191    where
192        Err: From<DeError> + From<Error<Infallible>>,
193    {
194        if let Error::DeserializeFailed(de_err) = self {
195            return de_err.into();
196        }
197
198        self.into_never_de_err().into()
199    }
200}
201
202/// XML error.
203#[derive(Debug, thiserror::Error)]
204#[error(transparent)]
205pub struct XMLError(#[from] quick_xml::Error);
206
207/// Implements conversion from [`Error`] with [`From`] for the given error type.
208///
209/// Allows for custom error types with source error types to easily be converted into with
210/// `?`.
211///
212/// The given error type should implement `From<Error<Infallible>>`.
213///
214/// # Examples
215/// ```
216/// use std::convert::Infallible;
217///
218/// use xml_stinks::deserializer::Error as DeserializerError;
219/// use xml_stinks::impl_from_deserializer_error;
220///
221/// #[derive(Debug, thiserror::Error)]
222/// enum FooError
223/// {
224///     #[error("Deserialization failed")]
225///     DeserializeFailed(#[from] DeserializerError<Infallible>),
226///
227///     #[error("Invalid bar")]
228///     InvalidBar(#[from] BarError),
229/// }
230///
231/// impl_from_deserializer_error!(FooError);
232///
233/// #[derive(Debug, thiserror::Error)]
234/// enum BarError
235/// {
236///     #[error("Oops")]
237///     Oops,
238/// }
239///
240/// let err_a: FooError = DeserializerError::<Infallible>::UnexpectedEndOfFile.into();
241///
242/// assert!(matches!(
243///     err_a,
244///     FooError::DeserializeFailed(DeserializerError::UnexpectedEndOfFile)
245/// ));
246///
247/// let err_b: FooError = DeserializerError::DeserializeFailed(BarError::Oops).into();
248///
249/// assert!(matches!(err_b, FooError::InvalidBar(BarError::Oops)));
250/// ```
251#[macro_export]
252macro_rules! impl_from_deserializer_error {
253    ($err: path) => {
254        impl<DeError: Into<Self>> From<::xml_stinks::deserializer::Error<DeError>>
255            for $err
256        {
257            fn from(err: ::xml_stinks::deserializer::Error<DeError>) -> Self
258            {
259                if let ::xml_stinks::deserializer::Error::DeserializeFailed(de_err) = err
260                {
261                    return de_err.into();
262                }
263
264                err.into_never_de_err().into()
265            }
266        }
267    };
268}
269
270feature_alternate!(
271    feature = "deserializer-static-generics",
272    /// Conditional compilation based on whether or not the `deserializer-static-generics`
273    /// feature is enabled.
274    ///
275    /// # Examples
276    /// ```
277    /// use std::io::Cursor;
278    ///
279    /// use xml_stinks::xml_stinks_if_deserializer_static_generics;
280    /// use xml_stinks::deserializer::buffered::Buffered as BufferedDeserializer;
281    /// use xml_stinks::deserializer::Deserializer;
282    ///
283    /// fn do_something(bytes: &[u8])
284    /// {
285    ///     let deserializer = xml_stinks_if_deserializer_static_generics!(then {
286    ///         BufferedDeserializer::new(Cursor::new(bytes.to_vec()));
287    ///     } else {
288    ///         // This wouldn't compile if the deserializer-static-generics feature was
289    ///         // enabled
290    ///         BufferedDeserializer::new(bytes);
291    ///     });
292    ///
293    ///     // ...
294    /// }
295    /// ```
296    when_enabled =
297        #[macro_export]
298        macro_rules! xml_stinks_if_deserializer_static_generics {
299            (then { $($then: tt)* }$(else { $($else: tt)* })?) => {
300                $($then)*
301            };
302        },
303    when_disabled =
304        #[macro_export]
305        macro_rules! xml_stinks_if_deserializer_static_generics {
306            (then { $($then: tt)* }$(else { $($else: tt)* })?) => {
307                $($($else)*)?
308            };
309        }
310);