lib.rs

  1#![forbid(missing_docs, unsafe_code)]
  2/*!
  3# XML Streamed Objects -- serde-like parsing for XML
  4
  5This crate provides the traits for parsing XML data into Rust structs, and
  6vice versa.
  7
  8While it is in 0.0.x versions, many features still need to be developed, but
  9rest assured that there is a solid plan to get it fully usable for even
 10advanced XML scenarios.
 11
 12XSO is an acronym for XML Stream(ed) Objects, referring to the main field of
 13use of this library in parsing XML streams like specified in RFC 6120.
 14*/
 15// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
 16//
 17// This Source Code Form is subject to the terms of the Mozilla Public
 18// License, v. 2.0. If a copy of the MPL was not distributed with this
 19// file, You can obtain one at http://mozilla.org/MPL/2.0/.
 20pub mod error;
 21pub mod minidom_compat;
 22
 23#[doc(hidden)]
 24pub mod exports {
 25    pub use rxml;
 26}
 27
 28/// Trait allowing to consume a struct and iterate its contents as
 29/// serialisable [`rxml::Event`] items.
 30pub trait IntoXml {
 31    /// The iterator type.
 32    type EventIter: Iterator<Item = Result<rxml::Event, self::error::Error>>;
 33
 34    /// Return an iterator which emits the contents of the struct or enum as
 35    /// serialisable [`rxml::Event`] items.
 36    fn into_event_iter(self) -> Result<Self::EventIter, self::error::Error>;
 37}
 38
 39/// Trait for a temporary object allowing to construct a struct from
 40/// [`rxml::Event`] items.
 41///
 42/// Objects of this type are generally constructed through
 43/// [`FromXml::from_events`] and are used to build Rust structs or enums from
 44/// XML data. The XML data must be fed as `rxml::Event` to the
 45/// [`feed`][`Self::feed`] method.
 46pub trait FromEventsBuilder {
 47    /// The type which will be constructed by this builder.
 48    type Output;
 49
 50    /// Feed another [`rxml::Event`] into the element construction
 51    /// process.
 52    ///
 53    /// Once the construction process completes, `Ok(Some(_))` is returned.
 54    /// When valid data has been fed but more events are needed to fully
 55    /// construct the resulting struct, `Ok(None)` is returned.
 56    ///
 57    /// If the construction fails, `Err(_)` is returned. Errors are generally
 58    /// fatal and the builder should be assumed to be broken at that point.
 59    /// Feeding more events after an error may result in panics, errors or
 60    /// inconsistent result data, though it may never result in unsound or
 61    /// unsafe behaviour.
 62    fn feed(&mut self, ev: rxml::Event) -> Result<Option<Self::Output>, self::error::Error>;
 63}
 64
 65/// Trait allowing to construct a struct from a stream of
 66/// [`rxml::Event`] items.
 67///
 68/// To use this, first call [`FromXml::from_events`] with the qualified
 69/// name and the attributes of the corresponding
 70/// [`rxml::Event::StartElement`] event. If the call succeeds, the
 71/// returned builder object must be fed with the events representing the
 72/// contents of the element, and then with the `EndElement` event.
 73///
 74/// The `StartElement` passed to `from_events` must not be passed to `feed`.
 75///
 76/// **Important:** Changing the [`Builder`][`Self::Builder`] associated type
 77/// is considered a non-breaking change for any given implementation of this
 78/// trait. Always refer to a type's builder type using fully-qualified
 79/// notation, for example: `<T as xso::FromXml>::Builder`.
 80pub trait FromXml {
 81    /// A builder type used to construct the element.
 82    ///
 83    /// **Important:** Changing this type is considered a non-breaking change
 84    /// for any given implementation of this trait. Always refer to a type's
 85    /// builder type using fully-qualified notation, for example:
 86    /// `<T as xso::FromXml>::Builder`.
 87    type Builder: FromEventsBuilder<Output = Self>;
 88
 89    /// Attempt to initiate the streamed construction of this struct from XML.
 90    ///
 91    /// If the passed qualified `name` and `attrs` match the element's type,
 92    /// the [`Self::Builder`] is returned and should be fed with XML events
 93    /// by the caller.
 94    ///
 95    /// Otherwise, an appropriate error is returned.
 96    fn from_events(
 97        name: rxml::QName,
 98        attrs: rxml::AttrMap,
 99    ) -> Result<Self::Builder, self::error::FromEventsError>;
100}
101
102/// Attempt to transform a type implementing [`IntoXml`] into another
103/// type which implements [`FromXml`].
104pub fn transform<T: FromXml, F: IntoXml>(from: F) -> Result<T, self::error::Error> {
105    let mut iter = from.into_event_iter()?;
106    let (qname, attrs) = match iter.next() {
107        Some(Ok(rxml::Event::StartElement(_, qname, attrs))) => (qname, attrs),
108        Some(Err(e)) => return Err(e),
109        _ => panic!("into_event_iter did not start with StartElement event!"),
110    };
111    let mut sink = match T::from_events(qname, attrs) {
112        Ok(v) => v,
113        Err(self::error::FromEventsError::Mismatch { .. }) => {
114            return Err(self::error::Error::TypeMismatch)
115        }
116        Err(self::error::FromEventsError::Invalid(e)) => return Err(e),
117    };
118    for event in iter {
119        let event = event?;
120        match sink.feed(event)? {
121            Some(v) => return Ok(v),
122            None => (),
123        }
124    }
125    Err(self::error::Error::XmlError(
126        rxml::error::XmlError::InvalidEof("during transform"),
127    ))
128}