fromxml.rs

  1//! # Generic builder type implementations
  2//!
  3//! This module contains [`FromEventsBuilder`] implementations for types from
  4//! foreign libraries (such as the standard library).
  5//!
  6//! In order to not clutter the `xso` crate's main namespace, they are
  7//! stashed away in a separate module.
  8
  9// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
 10//
 11// This Source Code Form is subject to the terms of the Mozilla Public
 12// License, v. 2.0. If a copy of the MPL was not distributed with this
 13// file, You can obtain one at http://mozilla.org/MPL/2.0/.
 14
 15use alloc::boxed::Box;
 16
 17use crate::error::{Error, FromEventsError};
 18use crate::{FromEventsBuilder, FromXml};
 19
 20/// Helper struct to construct an `Option<T>` from XML events.
 21pub struct OptionBuilder<T: FromEventsBuilder>(T);
 22
 23impl<T: FromEventsBuilder> FromEventsBuilder for OptionBuilder<T> {
 24    type Output = Option<T::Output>;
 25
 26    fn feed(&mut self, ev: rxml::Event) -> Result<Option<Self::Output>, Error> {
 27        self.0.feed(ev).map(|ok| ok.map(|value| Some(value)))
 28    }
 29}
 30
 31/// Parsers `T` into `Some(.)`.
 32///
 33/// Note that this never generates `None`: The main use case is to allow
 34/// external (i.e. without calling `from_events`) defaulting to `None` and
 35/// for optional serialisation (the [`AsXml`][`crate::AsXml`] implementation
 36/// on `Option<T>` emits nothing for `None`).
 37impl<T: FromXml> FromXml for Option<T> {
 38    type Builder = OptionBuilder<T::Builder>;
 39
 40    fn from_events(
 41        name: rxml::QName,
 42        attrs: rxml::AttrMap,
 43    ) -> Result<Self::Builder, FromEventsError> {
 44        Ok(OptionBuilder(T::from_events(name, attrs)?))
 45    }
 46}
 47
 48/// Helper struct to construct an `Box<T>` from XML events.
 49pub struct BoxBuilder<T: FromEventsBuilder>(Box<T>);
 50
 51impl<T: FromEventsBuilder> FromEventsBuilder for BoxBuilder<T> {
 52    type Output = Box<T::Output>;
 53
 54    fn feed(&mut self, ev: rxml::Event) -> Result<Option<Self::Output>, Error> {
 55        self.0.feed(ev).map(|ok| ok.map(|value| Box::new(value)))
 56    }
 57}
 58
 59/// Parsers `T` into a `Box`.
 60impl<T: FromXml> FromXml for Box<T> {
 61    type Builder = BoxBuilder<T::Builder>;
 62
 63    fn from_events(
 64        name: rxml::QName,
 65        attrs: rxml::AttrMap,
 66    ) -> Result<Self::Builder, FromEventsError> {
 67        Ok(BoxBuilder(Box::new(T::from_events(name, attrs)?)))
 68    }
 69}
 70
 71#[derive(Debug)]
 72enum FallibleBuilderInner<T: FromEventsBuilder, E> {
 73    Processing { depth: usize, builder: T },
 74    Failed { depth: usize, err: Option<E> },
 75    Done,
 76}
 77
 78/// Build a `Result<T, E>` from XML.
 79///
 80/// This builder, invoked generally via the [`FromXml`] implementation on
 81/// `Result<T, E> where T: FromXml, E: From<Error>`, allows to fallably parse
 82/// an XSO from XML.
 83///
 84/// If an error occurs while parsing the XSO, the remaining events which
 85/// belong to that XSO are discarded. Once all events have been seen, the
 86/// error is returned as `Err(.)` value.
 87///
 88/// If parsing succeeds, the parsed XSO is returned as `Ok(.)` value.
 89#[derive(Debug)]
 90pub struct FallibleBuilder<T: FromEventsBuilder, E>(FallibleBuilderInner<T, E>);
 91
 92impl<T: FromEventsBuilder, E: From<Error>> FromEventsBuilder for FallibleBuilder<T, E> {
 93    type Output = Result<T::Output, E>;
 94
 95    fn feed(&mut self, ev: rxml::Event) -> Result<Option<Self::Output>, Error> {
 96        match self.0 {
 97            FallibleBuilderInner::Processing {
 98                ref mut depth,
 99                ref mut builder,
100            } => {
101                let new_depth = match ev {
102                    rxml::Event::StartElement(..) => match depth.checked_add(1) {
103                        // I *think* it is OK to return an err here
104                        // instead of panicking. The reason is that anyone
105                        // who intends to resume processing at the level
106                        // of where we started to parse this thing in case
107                        // of an error either has to:
108                        // - Use this fallible implementation and rely on
109                        //   it capturing the error (which we don't in
110                        //   this case).
111                        // - Or count the depth themselves, which will
112                        //   either fail in the same way, or they use a
113                        //   wider type (in which case it's ok).
114                        None => {
115                            self.0 = FallibleBuilderInner::Done;
116                            return Err(Error::Other("maximum XML nesting depth exceeded"));
117                        }
118                        Some(v) => Some(v),
119                    },
120                    // In case of an element end, underflow means that we
121                    // have reached the end of the XSO we wanted to process.
122                    // We handle that case at the end of the outer match's
123                    // body: Either we have returned a value then (good), or,
124                    // if we reach the end there with a new_depth == None,
125                    // something went horribly wrong (and we panic).
126                    rxml::Event::EndElement(..) => depth.checked_sub(1),
127
128                    // Text and XML declarations have no influence on parsing
129                    // depth.
130                    rxml::Event::XmlDeclaration(..) | rxml::Event::Text(..) => Some(*depth),
131                };
132
133                match builder.feed(ev) {
134                    Ok(Some(v)) => {
135                        self.0 = FallibleBuilderInner::Done;
136                        return Ok(Some(Ok(v)));
137                    }
138                    Ok(None) => {
139                        // continue processing in the next round.
140                    }
141                    Err(e) => {
142                        // We are now officially failed ..
143                        match new_depth {
144                            // .. but we are not done yet, so enter the
145                            // failure backtracking state.
146                            Some(depth) => {
147                                self.0 = FallibleBuilderInner::Failed {
148                                    depth,
149                                    err: Some(e.into()),
150                                };
151                                return Ok(None);
152                            }
153                            // .. and we are done with parsing, so we return
154                            // the error as value.
155                            None => {
156                                self.0 = FallibleBuilderInner::Done;
157                                return Ok(Some(Err(e.into())));
158                            }
159                        }
160                    }
161                };
162
163                *depth = match new_depth {
164                    Some(v) => v,
165                    None => unreachable!("fallible parsing continued beyond end of element"),
166                };
167
168                // Need more events.
169                Ok(None)
170            }
171            FallibleBuilderInner::Failed {
172                ref mut depth,
173                ref mut err,
174            } => {
175                *depth = match ev {
176                    rxml::Event::StartElement(..) => match depth.checked_add(1) {
177                        // See above for error return rationale.
178                        None => {
179                            self.0 = FallibleBuilderInner::Done;
180                            return Err(Error::Other("maximum XML nesting depth exceeded"));
181                        }
182                        Some(v) => v,
183                    },
184                    rxml::Event::EndElement(..) => match depth.checked_sub(1) {
185                        Some(v) => v,
186                        None => {
187                            // We are officially done, return a value, switch
188                            // states, and be done with it.
189                            let err = err.take().expect("fallible parsing somehow lost its error");
190                            self.0 = FallibleBuilderInner::Done;
191                            return Ok(Some(Err(err)));
192                        }
193                    },
194
195                    // Text and XML declarations have no influence on parsing
196                    // depth.
197                    rxml::Event::XmlDeclaration(..) | rxml::Event::Text(..) => *depth,
198                };
199
200                // Need more events
201                Ok(None)
202            }
203            FallibleBuilderInner::Done => {
204                panic!("FromEventsBuilder called after it returned a value")
205            }
206        }
207    }
208}
209
210/// Parsers `T` fallibly. See [`FallibleBuilder`] for details.
211impl<T: FromXml, E: From<Error>> FromXml for Result<T, E> {
212    type Builder = FallibleBuilder<T::Builder, E>;
213
214    fn from_events(
215        name: rxml::QName,
216        attrs: rxml::AttrMap,
217    ) -> Result<Self::Builder, FromEventsError> {
218        match T::from_events(name, attrs) {
219            Ok(builder) => Ok(FallibleBuilder(FallibleBuilderInner::Processing {
220                depth: 0,
221                builder,
222            })),
223            Err(FromEventsError::Mismatch { name, attrs }) => {
224                Err(FromEventsError::Mismatch { name, attrs })
225            }
226            Err(FromEventsError::Invalid(e)) => Ok(FallibleBuilder(FallibleBuilderInner::Failed {
227                depth: 0,
228                err: Some(e.into()),
229            })),
230        }
231    }
232}
233
234/// Builder which discards an entire child tree without inspecting the
235/// contents.
236#[derive(Debug)]
237pub struct Discard {
238    depth: usize,
239}
240
241impl Discard {
242    /// Create a new discarding builder.
243    pub fn new() -> Self {
244        Self { depth: 0 }
245    }
246}
247
248impl FromEventsBuilder for Discard {
249    type Output = ();
250
251    fn feed(&mut self, ev: rxml::Event) -> Result<Option<Self::Output>, Error> {
252        match ev {
253            rxml::Event::StartElement(..) => {
254                self.depth = match self.depth.checked_add(1) {
255                    Some(v) => v,
256                    None => return Err(Error::Other("maximum XML nesting depth exceeded")),
257                };
258                Ok(None)
259            }
260            rxml::Event::EndElement(..) => match self.depth.checked_sub(1) {
261                None => Ok(Some(())),
262                Some(v) => {
263                    self.depth = v;
264                    Ok(None)
265                }
266            },
267            _ => Ok(None),
268        }
269    }
270}
271
272#[cfg(test)]
273mod tests {
274    use super::*;
275
276    use alloc::borrow::ToOwned;
277    use rxml::{parser::EventMetrics, Event, Namespace, NcName};
278
279    macro_rules! null_builder {
280        ($name:ident for $output:ident) => {
281            #[derive(Debug)]
282            enum $name {}
283
284            impl FromEventsBuilder for $name {
285                type Output = $output;
286
287                fn feed(&mut self, _: Event) -> Result<Option<Self::Output>, Error> {
288                    unreachable!();
289                }
290            }
291        };
292    }
293
294    null_builder!(AlwaysMismatchBuilder for AlwaysMismatch);
295    null_builder!(InitialErrorBuilder for InitialError);
296
297    #[derive(Debug)]
298    struct AlwaysMismatch;
299
300    impl FromXml for AlwaysMismatch {
301        type Builder = AlwaysMismatchBuilder;
302
303        fn from_events(
304            name: rxml::QName,
305            attrs: rxml::AttrMap,
306        ) -> Result<Self::Builder, FromEventsError> {
307            Err(FromEventsError::Mismatch { name, attrs })
308        }
309    }
310
311    #[derive(Debug)]
312    struct InitialError;
313
314    impl FromXml for InitialError {
315        type Builder = InitialErrorBuilder;
316
317        fn from_events(_: rxml::QName, _: rxml::AttrMap) -> Result<Self::Builder, FromEventsError> {
318            Err(FromEventsError::Invalid(Error::Other("some error")))
319        }
320    }
321
322    #[derive(Debug)]
323    struct FailOnContentBuilder;
324
325    impl FromEventsBuilder for FailOnContentBuilder {
326        type Output = FailOnContent;
327
328        fn feed(&mut self, _: Event) -> Result<Option<Self::Output>, Error> {
329            Err(Error::Other("content error"))
330        }
331    }
332
333    #[derive(Debug)]
334    struct FailOnContent;
335
336    impl FromXml for FailOnContent {
337        type Builder = FailOnContentBuilder;
338
339        fn from_events(_: rxml::QName, _: rxml::AttrMap) -> Result<Self::Builder, FromEventsError> {
340            Ok(FailOnContentBuilder)
341        }
342    }
343
344    fn qname() -> rxml::QName {
345        (Namespace::NONE, NcName::try_from("test").unwrap())
346    }
347
348    fn attrs() -> rxml::AttrMap {
349        rxml::AttrMap::new()
350    }
351
352    #[test]
353    fn fallible_builder_mismatch_passthrough() {
354        match Result::<AlwaysMismatch, Error>::from_events(qname(), attrs()) {
355            Err(FromEventsError::Mismatch { .. }) => (),
356            other => panic!("unexpected result: {:?}", other),
357        }
358    }
359
360    #[test]
361    fn fallible_builder_initial_error_capture() {
362        let mut builder = match Result::<InitialError, Error>::from_events(qname(), attrs()) {
363            Ok(v) => v,
364            other => panic!("unexpected result: {:?}", other),
365        };
366        match builder.feed(Event::Text(EventMetrics::zero(), "hello world!".to_owned())) {
367            Ok(None) => (),
368            other => panic!("unexpected result: {:?}", other),
369        };
370        match builder.feed(Event::EndElement(EventMetrics::zero())) {
371            Ok(Some(Err(Error::Other("some error")))) => (),
372            other => panic!("unexpected result: {:?}", other),
373        };
374    }
375
376    #[test]
377    fn fallible_builder_initial_error_capture_allows_nested_stuff() {
378        let mut builder = match Result::<InitialError, Error>::from_events(qname(), attrs()) {
379            Ok(v) => v,
380            other => panic!("unexpected result: {:?}", other),
381        };
382        match builder.feed(Event::StartElement(EventMetrics::zero(), qname(), attrs())) {
383            Ok(None) => (),
384            other => panic!("unexpected result: {:?}", other),
385        };
386        match builder.feed(Event::Text(EventMetrics::zero(), "hello world!".to_owned())) {
387            Ok(None) => (),
388            other => panic!("unexpected result: {:?}", other),
389        };
390        match builder.feed(Event::EndElement(EventMetrics::zero())) {
391            Ok(None) => (),
392            other => panic!("unexpected result: {:?}", other),
393        };
394        match builder.feed(Event::Text(EventMetrics::zero(), "hello world!".to_owned())) {
395            Ok(None) => (),
396            other => panic!("unexpected result: {:?}", other),
397        };
398        match builder.feed(Event::StartElement(EventMetrics::zero(), qname(), attrs())) {
399            Ok(None) => (),
400            other => panic!("unexpected result: {:?}", other),
401        };
402        match builder.feed(Event::StartElement(EventMetrics::zero(), qname(), attrs())) {
403            Ok(None) => (),
404            other => panic!("unexpected result: {:?}", other),
405        };
406        match builder.feed(Event::Text(EventMetrics::zero(), "hello world!".to_owned())) {
407            Ok(None) => (),
408            other => panic!("unexpected result: {:?}", other),
409        };
410        match builder.feed(Event::EndElement(EventMetrics::zero())) {
411            Ok(None) => (),
412            other => panic!("unexpected result: {:?}", other),
413        };
414        match builder.feed(Event::EndElement(EventMetrics::zero())) {
415            Ok(None) => (),
416            other => panic!("unexpected result: {:?}", other),
417        };
418        match builder.feed(Event::EndElement(EventMetrics::zero())) {
419            Ok(Some(Err(Error::Other("some error")))) => (),
420            other => panic!("unexpected result: {:?}", other),
421        };
422    }
423
424    #[test]
425    fn fallible_builder_content_error_capture() {
426        let mut builder = match Result::<FailOnContent, Error>::from_events(qname(), attrs()) {
427            Ok(v) => v,
428            other => panic!("unexpected result: {:?}", other),
429        };
430        match builder.feed(Event::EndElement(EventMetrics::zero())) {
431            Ok(Some(Err(Error::Other("content error")))) => (),
432            other => panic!("unexpected result: {:?}", other),
433        };
434    }
435
436    #[test]
437    fn fallible_builder_content_error_capture_with_more_content() {
438        let mut builder = match Result::<FailOnContent, Error>::from_events(qname(), attrs()) {
439            Ok(v) => v,
440            other => panic!("unexpected result: {:?}", other),
441        };
442        match builder.feed(Event::Text(EventMetrics::zero(), "hello world!".to_owned())) {
443            Ok(None) => (),
444            other => panic!("unexpected result: {:?}", other),
445        };
446        match builder.feed(Event::EndElement(EventMetrics::zero())) {
447            Ok(Some(Err(Error::Other("content error")))) => (),
448            other => panic!("unexpected result: {:?}", other),
449        };
450    }
451
452    #[test]
453    fn fallible_builder_content_error_capture_with_nested_content() {
454        let mut builder = match Result::<FailOnContent, Error>::from_events(qname(), attrs()) {
455            Ok(v) => v,
456            other => panic!("unexpected result: {:?}", other),
457        };
458        match builder.feed(Event::StartElement(EventMetrics::zero(), qname(), attrs())) {
459            Ok(None) => (),
460            other => panic!("unexpected result: {:?}", other),
461        };
462        match builder.feed(Event::Text(EventMetrics::zero(), "hello world!".to_owned())) {
463            Ok(None) => (),
464            other => panic!("unexpected result: {:?}", other),
465        };
466        match builder.feed(Event::EndElement(EventMetrics::zero())) {
467            Ok(None) => (),
468            other => panic!("unexpected result: {:?}", other),
469        };
470        match builder.feed(Event::Text(EventMetrics::zero(), "hello world!".to_owned())) {
471            Ok(None) => (),
472            other => panic!("unexpected result: {:?}", other),
473        };
474        match builder.feed(Event::StartElement(EventMetrics::zero(), qname(), attrs())) {
475            Ok(None) => (),
476            other => panic!("unexpected result: {:?}", other),
477        };
478        match builder.feed(Event::StartElement(EventMetrics::zero(), qname(), attrs())) {
479            Ok(None) => (),
480            other => panic!("unexpected result: {:?}", other),
481        };
482        match builder.feed(Event::Text(EventMetrics::zero(), "hello world!".to_owned())) {
483            Ok(None) => (),
484            other => panic!("unexpected result: {:?}", other),
485        };
486        match builder.feed(Event::EndElement(EventMetrics::zero())) {
487            Ok(None) => (),
488            other => panic!("unexpected result: {:?}", other),
489        };
490        match builder.feed(Event::EndElement(EventMetrics::zero())) {
491            Ok(None) => (),
492            other => panic!("unexpected result: {:?}", other),
493        };
494        match builder.feed(Event::EndElement(EventMetrics::zero())) {
495            Ok(Some(Err(Error::Other("content error")))) => (),
496            other => panic!("unexpected result: {:?}", other),
497        };
498    }
499}