xso: move helper iterators and builders into separate module

Jonas Schäfer created

This declutters the main `xso` namespace. In addition, if (when) we
introduce more complex generic implementations, we might want to have
tests for these, and those can then live there, too, without making the
main `lib.rs` file gigantic (or moving the tests too far away from the
tested code).

Change summary

xso-proc/src/field/child.rs |  2 
xso-proc/src/types.rs       |  4 +
xso/src/asxml.rs            | 69 +++++++++++++++++++++++++++
xso/src/fromxml.rs          | 60 +++++++++++++++++++++++
xso/src/lib.rs              | 98 --------------------------------------
5 files changed, 136 insertions(+), 97 deletions(-)

Detailed changes

xso-proc/src/field/child.rs 🔗

@@ -389,7 +389,7 @@ impl ExtractDef {
             // corresponding to this code above, and we will not repeat it
             // here.
             quote! {
-                ::xso::OptionAsXml::new(::core::option::Option::from(#bound_name).map(|#bound_name| {
+                ::xso::asxml::OptionAsXml::new(::core::option::Option::from(#bound_name).map(|#bound_name| {
                     #item_iter_ty_ident::new((#bound_name,))
                 }).transpose()?)
             },

xso-proc/src/types.rs 🔗

@@ -768,6 +768,10 @@ pub(crate) fn option_as_xml_ty(inner_ty: Type) -> Type {
                     ident: Ident::new("xso", span),
                     arguments: PathArguments::None,
                 },
+                PathSegment {
+                    ident: Ident::new("asxml", span),
+                    arguments: PathArguments::None,
+                },
                 PathSegment {
                     ident: Ident::new("OptionAsXml", span),
                     arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {

xso/src/asxml.rs 🔗

@@ -0,0 +1,69 @@
+//! # Generic iterator type implementations
+//!
+//! This module contains [`AsXml`] iterator implementations for types from
+//! foreign libraries (such as the standard library).
+//!
+//! In order to not clutter the `xso` crate's main namespace, they are
+//! stashed away in a separate module.
+
+// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+use crate::error::Error;
+use crate::rxml_util::Item;
+use crate::AsXml;
+
+/// Helper iterator to convert an `Option<T>` to XML.
+pub struct OptionAsXml<T: Iterator>(Option<T>);
+
+impl<T: Iterator> OptionAsXml<T> {
+    /// Construct a new iterator, wrapping the given iterator.
+    ///
+    /// If `inner` is `None`, this iterator terminates immediately. Otherwise,
+    /// it yields the elements yielded by `inner` until `inner` finishes,
+    /// after which this iterator completes, too.
+    pub fn new(inner: Option<T>) -> Self {
+        Self(inner)
+    }
+}
+
+impl<'x, T: Iterator<Item = Result<Item<'x>, Error>>> Iterator for OptionAsXml<T> {
+    type Item = Result<Item<'x>, Error>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.0.as_mut()?.next()
+    }
+}
+
+impl<T: AsXml> AsXml for Option<T> {
+    type ItemIter<'x> = OptionAsXml<T::ItemIter<'x>> where T: 'x;
+
+    fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
+        match self {
+            Some(ref value) => Ok(OptionAsXml(Some(T::as_xml_iter(value)?))),
+            None => Ok(OptionAsXml(None)),
+        }
+    }
+}
+
+/// Helper iterator to convert an `Box<T>` to XML.
+pub struct BoxAsXml<T: Iterator>(Box<T>);
+
+impl<'x, T: Iterator<Item = Result<Item<'x>, Error>>> Iterator for BoxAsXml<T> {
+    type Item = Result<Item<'x>, Error>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.0.next()
+    }
+}
+
+impl<T: AsXml> AsXml for Box<T> {
+    type ItemIter<'x> = BoxAsXml<T::ItemIter<'x>> where T: 'x;
+
+    fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
+        Ok(BoxAsXml(Box::new(T::as_xml_iter(&self)?)))
+    }
+}

xso/src/fromxml.rs 🔗

@@ -0,0 +1,60 @@
+//! # Generic builder type implementations
+//!
+//! This module contains [`FromEventsBuilder`] implementations for types from
+//! foreign libraries (such as the standard library).
+//!
+//! In order to not clutter the `xso` crate's main namespace, they are
+//! stashed away in a separate module.
+
+// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+use crate::error::{Error, FromEventsError};
+use crate::{FromEventsBuilder, FromXml};
+
+/// Helper struct to construct an `Option<T>` from XML events.
+pub struct OptionBuilder<T: FromEventsBuilder>(T);
+
+impl<T: FromEventsBuilder> FromEventsBuilder for OptionBuilder<T> {
+    type Output = Option<T::Output>;
+
+    fn feed(&mut self, ev: rxml::Event) -> Result<Option<Self::Output>, Error> {
+        self.0.feed(ev).map(|ok| ok.map(|value| Some(value)))
+    }
+}
+
+impl<T: FromXml> FromXml for Option<T> {
+    type Builder = OptionBuilder<T::Builder>;
+
+    fn from_events(
+        name: rxml::QName,
+        attrs: rxml::AttrMap,
+    ) -> Result<Self::Builder, FromEventsError> {
+        Ok(OptionBuilder(T::from_events(name, attrs)?))
+    }
+}
+
+/// Helper struct to construct an `Box<T>` from XML events.
+pub struct BoxBuilder<T: FromEventsBuilder>(Box<T>);
+
+impl<T: FromEventsBuilder> FromEventsBuilder for BoxBuilder<T> {
+    type Output = Box<T::Output>;
+
+    fn feed(&mut self, ev: rxml::Event) -> Result<Option<Self::Output>, Error> {
+        self.0.feed(ev).map(|ok| ok.map(|value| Box::new(value)))
+    }
+}
+
+impl<T: FromXml> FromXml for Box<T> {
+    type Builder = BoxBuilder<T::Builder>;
+
+    fn from_events(
+        name: rxml::QName,
+        attrs: rxml::AttrMap,
+    ) -> Result<Self::Builder, FromEventsError> {
+        Ok(BoxBuilder(Box::new(T::from_events(name, attrs)?)))
+    }
+}

xso/src/lib.rs 🔗

@@ -21,7 +21,9 @@ use of this library in parsing XML streams like specified in RFC 6120.
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
+pub mod asxml;
 pub mod error;
+pub mod fromxml;
 #[cfg(feature = "minidom")]
 pub mod minidom_compat;
 mod rxml_util;
@@ -81,58 +83,6 @@ pub trait AsXml {
     fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, self::error::Error>;
 }
 
-/// Helper iterator to convert an `Option<T>` to XML.
-pub struct OptionAsXml<T: Iterator>(Option<T>);
-
-impl<T: Iterator> OptionAsXml<T> {
-    /// Construct a new iterator, wrapping the given iterator.
-    ///
-    /// If `inner` is `None`, this iterator terminates immediately. Otherwise,
-    /// it yields the elements yielded by `inner` until `inner` finishes,
-    /// after which this iterator completes, too.
-    pub fn new(inner: Option<T>) -> Self {
-        Self(inner)
-    }
-}
-
-impl<'x, T: Iterator<Item = Result<Item<'x>, self::error::Error>>> Iterator for OptionAsXml<T> {
-    type Item = Result<Item<'x>, self::error::Error>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.0.as_mut()?.next()
-    }
-}
-
-/// Helper iterator to convert an `Box<T>` to XML.
-pub struct BoxAsXml<T: Iterator>(Box<T>);
-
-impl<'x, T: Iterator<Item = Result<Item<'x>, self::error::Error>>> Iterator for BoxAsXml<T> {
-    type Item = Result<Item<'x>, self::error::Error>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.0.next()
-    }
-}
-
-impl<T: AsXml> AsXml for Option<T> {
-    type ItemIter<'x> = OptionAsXml<T::ItemIter<'x>> where T: 'x;
-
-    fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, self::error::Error> {
-        match self {
-            Some(ref value) => Ok(OptionAsXml(Some(T::as_xml_iter(value)?))),
-            None => Ok(OptionAsXml(None)),
-        }
-    }
-}
-
-impl<T: AsXml> AsXml for Box<T> {
-    type ItemIter<'x> = BoxAsXml<T::ItemIter<'x>> where T: 'x;
-
-    fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, self::error::Error> {
-        Ok(BoxAsXml(Box::new(T::as_xml_iter(&self)?)))
-    }
-}
-
 /// Trait for a temporary object allowing to construct a struct from
 /// [`rxml::Event`] items.
 ///
@@ -159,28 +109,6 @@ pub trait FromEventsBuilder {
     fn feed(&mut self, ev: rxml::Event) -> Result<Option<Self::Output>, self::error::Error>;
 }
 
-/// Helper struct to construct an `Option<T>` from XML events.
-pub struct OptionBuilder<T: FromEventsBuilder>(T);
-
-/// Helper struct to construct an `Box<T>` from XML events.
-pub struct BoxBuilder<T: FromEventsBuilder>(Box<T>);
-
-impl<T: FromEventsBuilder> FromEventsBuilder for OptionBuilder<T> {
-    type Output = Option<T::Output>;
-
-    fn feed(&mut self, ev: rxml::Event) -> Result<Option<Self::Output>, self::error::Error> {
-        self.0.feed(ev).map(|ok| ok.map(|value| Some(value)))
-    }
-}
-
-impl<T: FromEventsBuilder> FromEventsBuilder for BoxBuilder<T> {
-    type Output = Box<T::Output>;
-
-    fn feed(&mut self, ev: rxml::Event) -> Result<Option<Self::Output>, self::error::Error> {
-        self.0.feed(ev).map(|ok| ok.map(|value| Box::new(value)))
-    }
-}
-
 /// Trait allowing to construct a struct from a stream of
 /// [`rxml::Event`] items.
 ///
@@ -218,28 +146,6 @@ pub trait FromXml {
     ) -> Result<Self::Builder, self::error::FromEventsError>;
 }
 
-impl<T: FromXml> FromXml for Option<T> {
-    type Builder = OptionBuilder<T::Builder>;
-
-    fn from_events(
-        name: rxml::QName,
-        attrs: rxml::AttrMap,
-    ) -> Result<Self::Builder, self::error::FromEventsError> {
-        Ok(OptionBuilder(T::from_events(name, attrs)?))
-    }
-}
-
-impl<T: FromXml> FromXml for Box<T> {
-    type Builder = BoxBuilder<T::Builder>;
-
-    fn from_events(
-        name: rxml::QName,
-        attrs: rxml::AttrMap,
-    ) -> Result<Self::Builder, self::error::FromEventsError> {
-        Ok(BoxBuilder(Box::new(T::from_events(name, attrs)?)))
-    }
-}
-
 /// Trait allowing to convert XML text to a value.
 ///
 /// This trait is similar to [`core::str::FromStr`], however, due to