lib.rs

  1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
  2#![cfg_attr(docsrs, feature(doc_cfg))]
  3#![forbid(unsafe_code)]
  4#![warn(missing_docs)]
  5/*!
  6# XML Streamed Objects -- serde-like parsing for XML
  7
  8This crate provides the traits for parsing XML data into Rust structs, and
  9vice versa.
 10
 11While it is in 0.0.x versions, many features still need to be developed, but
 12rest assured that there is a solid plan to get it fully usable for even
 13advanced XML scenarios.
 14
 15XSO is an acronym for XML Stream(ed) Objects, referring to the main field of
 16use of this library in parsing XML streams like specified in RFC 6120.
 17*/
 18
 19// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
 20//
 21// This Source Code Form is subject to the terms of the Mozilla Public
 22// License, v. 2.0. If a copy of the MPL was not distributed with this
 23// file, You can obtain one at http://mozilla.org/MPL/2.0/.
 24
 25#![no_std]
 26
 27extern crate alloc;
 28#[cfg(feature = "std")]
 29extern crate std;
 30#[cfg(feature = "std")]
 31use std::io;
 32
 33pub mod asxml;
 34pub mod error;
 35pub mod fromxml;
 36#[cfg(feature = "minidom")]
 37pub mod minidom_compat;
 38mod rxml_util;
 39pub mod text;
 40
 41#[doc(hidden)]
 42#[cfg(feature = "macros")]
 43pub mod exports {
 44    #[cfg(feature = "minidom")]
 45    pub use minidom;
 46    pub use rxml;
 47
 48    // These re-exports are necessary to support both std and no_std in code
 49    // generated by the macros.
 50    //
 51    // If we attempted to use ::alloc directly from macros, std builds would
 52    // not work because alloc is not generally present in builds using std.
 53    // If we used ::std, no_std builds would obviously not work. By exporting
 54    // std as alloc in std builds, we can safely use the alloc types from
 55    // there.
 56    //
 57    // Obviously, we have to be careful in xso-proc to not refer to types
 58    // which are not in alloc.
 59    #[cfg(not(feature = "std"))]
 60    pub extern crate alloc;
 61    #[cfg(feature = "std")]
 62    pub extern crate std as alloc;
 63
 64    /// The built-in `bool` type.
 65    ///
 66    /// This is re-exported for use by macros in cases where we cannot rely on
 67    /// people not having done `type bool = str` or some similar shenanigans.
 68    pub type CoreBool = bool;
 69
 70    /// The built-in `u8` type.
 71    ///
 72    /// This is re-exported for use by macros in cases where we cannot rely on
 73    /// people not having done `type u8 = str` or some similar shenanigans.
 74    pub type CoreU8 = u8;
 75
 76    /// Compile-time comparison of two strings.
 77    ///
 78    /// Used by macro-generated code.
 79    ///
 80    /// This is necessary because `<str as PartialEq>::eq` is not `const`.
 81    pub const fn const_str_eq(a: &'static str, b: &'static str) -> bool {
 82        let a = a.as_bytes();
 83        let b = b.as_bytes();
 84        if a.len() != b.len() {
 85            return false;
 86        }
 87
 88        let mut i = 0;
 89        while i < a.len() {
 90            if a[i] != b[i] {
 91                return false;
 92            }
 93            i += 1;
 94        }
 95
 96        true
 97    }
 98}
 99
100use alloc::{
101    borrow::{Cow, ToOwned},
102    boxed::Box,
103    string::String,
104    vec::Vec,
105};
106
107pub use text::TextCodec;
108
109#[doc(inline)]
110pub use rxml_util::Item;
111
112pub use asxml::PrintRawXml;
113
114#[doc = include_str!("from_xml_doc.md")]
115#[doc(inline)]
116#[cfg(feature = "macros")]
117pub use xso_proc::FromXml;
118
119/// # Make a struct or enum serialisable to XML
120///
121/// This derives the [`AsXml`] trait on a struct or enum. It is the
122/// counterpart to [`macro@FromXml`].
123///
124/// The attributes necessary and available for the derivation to work are
125/// documented on [`macro@FromXml`].
126#[doc(inline)]
127#[cfg(feature = "macros")]
128pub use xso_proc::AsXml;
129
130/// Trait allowing to iterate a struct's contents as serialisable
131/// [`Item`]s.
132///
133/// **Important:** Changing the [`ItemIter`][`Self::ItemIter`] associated
134/// type is considered a non-breaking change for any given implementation of
135/// this trait. Always refer to a type's iterator type using fully-qualified
136/// notation, for example: `<T as xso::AsXml>::ItemIter`.
137pub trait AsXml {
138    /// The iterator type.
139    ///
140    /// **Important:** Changing this type is considered a non-breaking change
141    /// for any given implementation of this trait. Always refer to a type's
142    /// iterator type using fully-qualified notation, for example:
143    /// `<T as xso::AsXml>::ItemIter`.
144    type ItemIter<'x>: Iterator<Item = Result<Item<'x>, self::error::Error>>
145    where
146        Self: 'x;
147
148    /// Return an iterator which emits the contents of the struct or enum as
149    /// serialisable [`Item`] items.
150    fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, self::error::Error>;
151}
152
153/// # Parsing context for [`FromEventsBuilder`]
154///
155/// For the most part, [`FromEventsBuilder`] implementations can work with
156/// only the information inside the [`rxml::Event`] which is delivered to
157/// them (and any information they may have stored from previous events).
158///
159/// However, there is (currently) one special case: the `xml:lang` attribute.
160/// That attribute is inherited across the entire document tree hierarchy. If
161/// the parsed element is not the top-level element, there may be an implicit
162/// value for `xml:lang`.
163#[derive(Debug)]
164pub struct Context<'x> {
165    language: Option<&'x str>,
166}
167
168impl<'x> Context<'x> {
169    /// A context suitable for the beginning of the document.
170    ///
171    /// `xml:lang` is assumed to be unset.
172    pub fn empty() -> Self {
173        Self { language: None }
174    }
175
176    /// Set the effective `xml:lang` value on the context and return it.
177    pub fn with_language(mut self, language: Option<&'x str>) -> Self {
178        self.language = language;
179        self
180    }
181
182    /// Return the `xml:lang` value in effect at the end of the event which
183    /// is currently being processed.
184    pub fn language(&self) -> Option<&str> {
185        self.language.as_deref()
186    }
187}
188
189/// Trait for a temporary object allowing to construct a struct from
190/// [`rxml::Event`] items.
191///
192/// Objects of this type are generally constructed through
193/// [`FromXml::from_events`] and are used to build Rust structs or enums from
194/// XML data. The XML data must be fed as `rxml::Event` to the
195/// [`feed`][`Self::feed`] method.
196pub trait FromEventsBuilder {
197    /// The type which will be constructed by this builder.
198    type Output;
199
200    /// Feed another [`rxml::Event`] into the element construction
201    /// process.
202    ///
203    /// Once the construction process completes, `Ok(Some(_))` is returned.
204    /// When valid data has been fed but more events are needed to fully
205    /// construct the resulting struct, `Ok(None)` is returned.
206    ///
207    /// If the construction fails, `Err(_)` is returned. Errors are generally
208    /// fatal and the builder should be assumed to be broken at that point.
209    /// Feeding more events after an error may result in panics, errors or
210    /// inconsistent result data, though it may never result in unsound or
211    /// unsafe behaviour.
212    fn feed(
213        &mut self,
214        ev: rxml::Event,
215        ctx: &Context<'_>,
216    ) -> Result<Option<Self::Output>, self::error::Error>;
217}
218
219/// Trait allowing to construct a struct from a stream of
220/// [`rxml::Event`] items.
221///
222/// To use this, first call [`FromXml::from_events`] with the qualified
223/// name and the attributes of the corresponding
224/// [`rxml::Event::StartElement`] event. If the call succeeds, the
225/// returned builder object must be fed with the events representing the
226/// contents of the element, and then with the `EndElement` event.
227///
228/// The `StartElement` passed to `from_events` must not be passed to `feed`.
229///
230/// **Important:** Changing the [`Builder`][`Self::Builder`] associated type
231/// is considered a non-breaking change for any given implementation of this
232/// trait. Always refer to a type's builder type using fully-qualified
233/// notation, for example: `<T as xso::FromXml>::Builder`.
234pub trait FromXml {
235    /// A builder type used to construct the element.
236    ///
237    /// **Important:** Changing this type is considered a non-breaking change
238    /// for any given implementation of this trait. Always refer to a type's
239    /// builder type using fully-qualified notation, for example:
240    /// `<T as xso::FromXml>::Builder`.
241    type Builder: FromEventsBuilder<Output = Self>;
242
243    /// Attempt to initiate the streamed construction of this struct from XML.
244    ///
245    /// If the passed qualified `name` and `attrs` match the element's type,
246    /// the [`Self::Builder`] is returned and should be fed with XML events
247    /// by the caller.
248    ///
249    /// Otherwise, an appropriate error is returned.
250    fn from_events(
251        name: rxml::QName,
252        attrs: rxml::AttrMap,
253        ctx: &Context<'_>,
254    ) -> Result<Self::Builder, self::error::FromEventsError>;
255}
256
257/// Trait allowing to convert XML text to a value.
258///
259/// This trait is similar to [`FromStr`][`core::str::FromStr`], however, to
260/// allow specialisation for XML<->Text conversion, a separate trait is
261/// introduced. Unlike `FromStr`, this trait allows taking ownership of the
262/// original text data, potentially saving allocations.
263///
264/// **Important:** See the [`text`][`crate::text`] module's documentation
265/// for notes regarding implementations for types from third-party crates.
266pub trait FromXmlText: Sized {
267    /// Convert the given XML text to a value.
268    fn from_xml_text(data: String) -> Result<Self, self::error::Error>;
269}
270
271impl FromXmlText for String {
272    /// Return the string unchanged.
273    fn from_xml_text(data: String) -> Result<Self, self::error::Error> {
274        Ok(data)
275    }
276}
277
278impl<T: FromXmlText, B: ToOwned<Owned = T>> FromXmlText for Cow<'_, B> {
279    /// Return a [`Cow::Owned`] containing the parsed value.
280    fn from_xml_text(data: String) -> Result<Self, self::error::Error> {
281        Ok(Cow::Owned(T::from_xml_text(data)?))
282    }
283}
284
285impl<T: FromXmlText> FromXmlText for Option<T> {
286    /// Return a [`Some`] containing the parsed value.
287    fn from_xml_text(data: String) -> Result<Self, self::error::Error> {
288        Ok(Some(T::from_xml_text(data)?))
289    }
290}
291
292impl<T: FromXmlText> FromXmlText for Box<T> {
293    /// Return a [`Box`] containing the parsed value.
294    fn from_xml_text(data: String) -> Result<Self, self::error::Error> {
295        Ok(Box::new(T::from_xml_text(data)?))
296    }
297}
298
299/// Trait to convert a value to an XML text string.
300///
301/// Implementing this trait for a type allows it to be used both for XML
302/// character data within elements and for XML attributes. For XML attributes,
303/// the behaviour is defined by [`AsXmlText::as_optional_xml_text`], while
304/// XML element text content uses [`AsXmlText::as_xml_text`]. Implementing
305/// [`AsXmlText`] automatically provides an implementation of
306/// [`AsOptionalXmlText`].
307///
308/// If your type should only be used in XML attributes and has no correct
309/// serialisation in XML text, you should *only* implement
310/// [`AsOptionalXmlText`] and omit the [`AsXmlText`] implementation.
311///
312/// **Important:** See the [`text`][`crate::text`] module's documentation
313/// for notes regarding implementations for types from third-party crates.
314pub trait AsXmlText {
315    /// Convert the value to an XML string in a context where an absent value
316    /// cannot be represented.
317    fn as_xml_text(&self) -> Result<Cow<'_, str>, self::error::Error>;
318
319    /// Convert the value to an XML string in a context where an absent value
320    /// can be represented.
321    ///
322    /// The provided implementation will always return the result of
323    /// [`Self::as_xml_text`] wrapped into `Some(.)`. By re-implementing
324    /// this method, implementors can customize the behaviour for certain
325    /// values.
326    fn as_optional_xml_text(&self) -> Result<Option<Cow<'_, str>>, self::error::Error> {
327        Ok(Some(self.as_xml_text()?))
328    }
329}
330
331impl AsXmlText for String {
332    /// Return the borrowed string contents.
333    fn as_xml_text(&self) -> Result<Cow<'_, str>, self::error::Error> {
334        Ok(Cow::Borrowed(self))
335    }
336}
337
338impl AsXmlText for str {
339    /// Return the borrowed string contents.
340    fn as_xml_text(&self) -> Result<Cow<'_, str>, self::error::Error> {
341        Ok(Cow::Borrowed(self))
342    }
343}
344
345impl<T: AsXmlText> AsXmlText for Box<T> {
346    /// Return the borrowed [`Box`] contents.
347    fn as_xml_text(&self) -> Result<Cow<'_, str>, self::error::Error> {
348        T::as_xml_text(self)
349    }
350}
351
352impl<B: AsXmlText + ToOwned> AsXmlText for Cow<'_, B> {
353    /// Return the borrowed [`Cow`] contents.
354    fn as_xml_text(&self) -> Result<Cow<'_, str>, self::error::Error> {
355        B::as_xml_text(self)
356    }
357}
358
359impl<T: AsXmlText> AsXmlText for &T {
360    /// Delegate to the `AsXmlText` implementation on `T`.
361    fn as_xml_text(&self) -> Result<Cow<'_, str>, self::error::Error> {
362        T::as_xml_text(*self)
363    }
364}
365
366/// Specialized variant of [`AsXmlText`].
367///
368/// Normally, it should not be necessary to implement this trait as it is
369/// automatically implemented for all types implementing [`AsXmlText`].
370/// However, if your type can only be serialised as an XML attribute (for
371/// example because an absent value has a particular meaning), it is correct
372/// to implement [`AsOptionalXmlText`] **instead of** [`AsXmlText`].
373///
374/// If your type can be serialised as both (text and attribute) but needs
375/// special handling in attributes, implement [`AsXmlText`] but provide a
376/// custom implementation of [`AsXmlText::as_optional_xml_text`].
377///
378/// **Important:** See the [`text`][`crate::text`] module's documentation
379/// for notes regarding implementations for types from third-party crates.
380pub trait AsOptionalXmlText {
381    /// Convert the value to an XML string in a context where an absent value
382    /// can be represented.
383    fn as_optional_xml_text(&self) -> Result<Option<Cow<'_, str>>, self::error::Error>;
384}
385
386impl<T: AsXmlText> AsOptionalXmlText for T {
387    fn as_optional_xml_text(&self) -> Result<Option<Cow<'_, str>>, self::error::Error> {
388        <Self as AsXmlText>::as_optional_xml_text(self)
389    }
390}
391
392impl<T: AsXmlText> AsOptionalXmlText for Option<T> {
393    fn as_optional_xml_text(&self) -> Result<Option<Cow<'_, str>>, self::error::Error> {
394        self.as_ref()
395            .map(T::as_optional_xml_text)
396            .transpose()
397            .map(Option::flatten)
398    }
399}
400
401/// Control how unknown attributes are handled.
402///
403/// The variants of this enum are referenced in the
404/// `#[xml(on_unknown_attribute = ..)]` which can be used on structs and
405/// enum variants. The specified variant controls how attributes, which are
406/// not handled by any member of the compound, are handled during parsing.
407#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
408pub enum UnknownAttributePolicy {
409    /// All unknown attributes are discarded.
410    ///
411    /// This is the default policy if the crate is built with the
412    /// `non-pedantic` feature.
413    #[cfg_attr(feature = "non-pedantic", default)]
414    Discard,
415
416    /// The first unknown attribute which is encountered generates a fatal
417    /// parsing error.
418    ///
419    /// This is the default policy if the crate is built **without** the
420    /// `non-pedantic` feature.
421    #[cfg_attr(not(feature = "non-pedantic"), default)]
422    Fail,
423}
424
425impl UnknownAttributePolicy {
426    #[doc(hidden)]
427    /// Implementation of the policy.
428    ///
429    /// This is an internal API and not subject to semver versioning.
430    pub fn apply_policy(&self, msg: &'static str) -> Result<(), self::error::Error> {
431        match self {
432            Self::Fail => Err(self::error::Error::Other(msg)),
433            Self::Discard => Ok(()),
434        }
435    }
436}
437
438/// Control how unknown children are handled.
439///
440/// The variants of this enum are referenced in the
441/// `#[xml(on_unknown_child = ..)]` which can be used on structs and
442/// enum variants. The specified variant controls how children, which are not
443/// handled by any member of the compound, are handled during parsing.
444#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
445pub enum UnknownChildPolicy {
446    /// All unknown children are discarded.
447    ///
448    /// This is the default policy if the crate is built with the
449    /// `non-pedantic` feature.
450    #[cfg_attr(feature = "non-pedantic", default)]
451    Discard,
452
453    /// The first unknown child which is encountered generates a fatal
454    /// parsing error.
455    ///
456    /// This is the default policy if the crate is built **without** the
457    /// `non-pedantic` feature.
458    #[cfg_attr(not(feature = "non-pedantic"), default)]
459    Fail,
460}
461
462impl UnknownChildPolicy {
463    #[doc(hidden)]
464    /// Implementation of the policy.
465    ///
466    /// This is an internal API and not subject to semver versioning.
467    pub fn apply_policy(&self, msg: &'static str) -> Result<(), self::error::Error> {
468        match self {
469            Self::Fail => Err(self::error::Error::Other(msg)),
470            Self::Discard => Ok(()),
471        }
472    }
473}
474
475/// Attempt to transform a type implementing [`AsXml`] into another
476/// type which implements [`FromXml`].
477pub fn transform<T: FromXml, F: AsXml>(from: &F) -> Result<T, self::error::Error> {
478    let mut languages = rxml::xml_lang::XmlLangStack::new();
479    let mut iter = self::rxml_util::ItemToEvent::new(from.as_xml_iter()?);
480    let (qname, attrs) = match iter.next() {
481        Some(Ok(rxml::Event::StartElement(_, qname, attrs))) => (qname, attrs),
482        Some(Err(e)) => return Err(e),
483        _ => panic!("into_event_iter did not start with StartElement event!"),
484    };
485    languages.push_from_attrs(&attrs);
486    let mut sink = match T::from_events(
487        qname,
488        attrs,
489        &Context::empty().with_language(languages.current()),
490    ) {
491        Ok(v) => v,
492        Err(self::error::FromEventsError::Mismatch { .. }) => {
493            return Err(self::error::Error::TypeMismatch)
494        }
495        Err(self::error::FromEventsError::Invalid(e)) => return Err(e),
496    };
497    for event in iter {
498        let event = event?;
499        languages.handle_event(&event);
500        if let Some(v) = sink.feed(event, &Context::empty().with_language(languages.current()))? {
501            return Ok(v);
502        }
503    }
504    Err(self::error::Error::XmlError(rxml::Error::InvalidEof(None)))
505}
506
507/// Attempt to convert a [`minidom::Element`] into a type implementing
508/// [`FromXml`], fallably.
509///
510/// Unlike [`transform`] (which can also be used with an element), this
511/// function will return the element unharmed if its element header does not
512/// match the expectations of `T`.
513#[cfg(feature = "minidom")]
514#[deprecated(
515    since = "0.1.3",
516    note = "obsolete since the transition to AsXml, which works by reference; use xso::transform instead."
517)]
518pub fn try_from_element<T: FromXml>(
519    from: minidom::Element,
520) -> Result<T, self::error::FromElementError> {
521    let mut languages = rxml::xml_lang::XmlLangStack::new();
522    #[allow(deprecated)]
523    let (qname, attrs) = minidom_compat::make_start_ev_parts(&from)?;
524
525    languages.push_from_attrs(&attrs);
526    let mut sink = match T::from_events(
527        qname,
528        attrs,
529        &Context::empty().with_language(languages.current()),
530    ) {
531        Ok(v) => v,
532        Err(self::error::FromEventsError::Mismatch { .. }) => {
533            return Err(self::error::FromElementError::Mismatch(from))
534        }
535        Err(self::error::FromEventsError::Invalid(e)) => {
536            return Err(self::error::FromElementError::Invalid(e))
537        }
538    };
539
540    let mut iter = from.as_xml_iter()?;
541    // consume the element header
542    for item in &mut iter {
543        let item = item?;
544        match item {
545            // discard the element header
546            Item::XmlDeclaration(..) => (),
547            Item::ElementHeadStart(..) => (),
548            Item::Attribute(..) => (),
549            Item::ElementHeadEnd => {
550                // now that the element header is over, we break out
551                break;
552            }
553            Item::Text(..) => panic!("text before end of element header"),
554            Item::ElementFoot => panic!("element foot before end of element header"),
555        }
556    }
557    let iter = self::rxml_util::ItemToEvent::new(iter);
558    for event in iter {
559        let event = event?;
560        languages.handle_event(&event);
561        if let Some(v) = sink.feed(event, &Context::empty().with_language(languages.current()))? {
562            return Ok(v);
563        }
564    }
565    // unreachable! instead of error here, because minidom::Element always
566    // produces the complete event sequence of a single element, and FromXml
567    // implementations must be constructible from that.
568    unreachable!("minidom::Element did not produce enough events to complete element")
569}
570
571/// Attempt to parse a type implementing [`FromXml`] from a byte buffer
572/// containing XML data.
573pub fn from_bytes<T: FromXml>(mut buf: &[u8]) -> Result<T, self::error::Error> {
574    use rxml::{error::EndOrError, Parse};
575
576    let mut languages = rxml::xml_lang::XmlLangStack::new();
577    let mut parser = rxml::Parser::new();
578    let (name, attrs) = loop {
579        match parser.parse(&mut buf, true) {
580            Ok(Some(rxml::Event::XmlDeclaration(_, rxml::XmlVersion::V1_0))) => (),
581            Ok(Some(rxml::Event::StartElement(_, name, attrs))) => break (name, attrs),
582            Err(EndOrError::Error(e)) => return Err(self::error::Error::XmlError(e)),
583            Ok(None) | Err(EndOrError::NeedMoreData) => {
584                return Err(self::error::Error::XmlError(rxml::Error::InvalidEof(Some(
585                    rxml::error::ErrorContext::DocumentBegin,
586                ))))
587            }
588            Ok(Some(_)) => {
589                return Err(self::error::Error::Other(
590                    "Unexpected event at start of document",
591                ))
592            }
593        }
594    };
595    languages.push_from_attrs(&attrs);
596    let mut builder = match T::from_events(
597        name,
598        attrs,
599        &Context::empty().with_language(languages.current()),
600    ) {
601        Ok(v) => v,
602        Err(self::error::FromEventsError::Mismatch { .. }) => {
603            return Err(self::error::Error::TypeMismatch);
604        }
605        Err(self::error::FromEventsError::Invalid(e)) => {
606            return Err(e);
607        }
608    };
609
610    loop {
611        match parser.parse(&mut buf, true) {
612            Ok(Some(ev)) => {
613                languages.handle_event(&ev);
614                if let Some(v) =
615                    builder.feed(ev, &Context::empty().with_language(languages.current()))?
616                {
617                    return Ok(v);
618                }
619            }
620            Err(EndOrError::Error(e)) => return Err(self::error::Error::XmlError(e)),
621            Ok(None) | Err(EndOrError::NeedMoreData) => {
622                return Err(self::error::Error::XmlError(rxml::Error::InvalidEof(None)))
623            }
624        }
625    }
626}
627
628#[cfg(feature = "std")]
629fn read_start_event_io(
630    r: &mut impl Iterator<Item = io::Result<rxml::Event>>,
631) -> io::Result<(rxml::QName, rxml::AttrMap)> {
632    for ev in r {
633        match ev? {
634            rxml::Event::XmlDeclaration(_, rxml::XmlVersion::V1_0) => (),
635            rxml::Event::StartElement(_, name, attrs) => return Ok((name, attrs)),
636            _ => {
637                return Err(io::Error::new(
638                    io::ErrorKind::InvalidData,
639                    self::error::Error::Other("Unexpected event at start of document"),
640                ))
641            }
642        }
643    }
644    Err(io::Error::new(
645        io::ErrorKind::InvalidData,
646        self::error::Error::XmlError(rxml::Error::InvalidEof(Some(
647            rxml::error::ErrorContext::DocumentBegin,
648        ))),
649    ))
650}
651
652/// Attempt to parse a type implementing [`FromXml`] from a reader.
653#[cfg(feature = "std")]
654pub fn from_reader<T: FromXml, R: io::BufRead>(r: R) -> io::Result<T> {
655    let mut reader = rxml::XmlLangTracker::wrap(rxml::Reader::new(r));
656    let (name, attrs) = read_start_event_io(&mut reader)?;
657    let mut builder = match T::from_events(
658        name,
659        attrs,
660        &Context::empty().with_language(reader.language()),
661    ) {
662        Ok(v) => v,
663        Err(self::error::FromEventsError::Mismatch { .. }) => {
664            return Err(self::error::Error::TypeMismatch)
665                .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
666        }
667        Err(self::error::FromEventsError::Invalid(e)) => {
668            return Err(e).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
669        }
670    };
671    while let Some(ev) = reader.next() {
672        if let Some(v) = builder
673            .feed(ev?, &Context::empty().with_language(reader.language()))
674            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
675        {
676            return Ok(v);
677        }
678    }
679    Err(io::Error::new(
680        io::ErrorKind::UnexpectedEof,
681        self::error::Error::XmlError(rxml::Error::InvalidEof(None)),
682    ))
683}
684
685/// Attempt to serialise a type implementing [`AsXml`] to a vector of bytes.
686pub fn to_vec<T: AsXml>(xso: &T) -> Result<Vec<u8>, self::error::Error> {
687    let iter = xso.as_xml_iter()?;
688    let mut writer = rxml::writer::Encoder::new();
689    let mut buf = Vec::new();
690    for item in iter {
691        let item = item?;
692        writer.encode(item.as_rxml_item(), &mut buf)?;
693    }
694    Ok(buf)
695}
696
697/// Return true if the string contains exclusively XML whitespace.
698///
699/// XML whitespace is defined as U+0020 (space), U+0009 (tab), U+000a
700/// (newline) and U+000d (carriage return).
701pub fn is_xml_whitespace<T: AsRef<[u8]>>(s: T) -> bool {
702    s.as_ref()
703        .iter()
704        .all(|b| *b == b' ' || *b == b'\t' || *b == b'\r' || *b == b'\n')
705}