From cd9f2033f3543068991a22a6b6078b89dfa08e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Sun, 30 Jun 2024 09:27:48 +0200 Subject: [PATCH] xso: add support for boxed children This allows building recursive tree structures. --- parsers/src/util/macro_tests.rs | 37 +++++++++++++++++++++++++++++ xso/ChangeLog | 4 ++-- xso/src/lib.rs | 41 +++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/parsers/src/util/macro_tests.rs b/parsers/src/util/macro_tests.rs index 1bc559ea..2ef80fc4 100644 --- a/parsers/src/util/macro_tests.rs +++ b/parsers/src/util/macro_tests.rs @@ -550,3 +550,40 @@ fn optional_child_roundtrip_absent() { }; roundtrip_full::("") } + +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] +#[xml(namespace = NS1, name = "elem")] +struct BoxedChild { + #[xml(child(default))] + child: std::option::Option>, +} + +#[test] +fn boxed_child_roundtrip_absent() { + #[allow(unused_imports)] + use std::{ + option::Option::{None, Some}, + result::Result::{Err, Ok}, + }; + roundtrip_full::("") +} + +#[test] +fn boxed_child_roundtrip_nested_1() { + #[allow(unused_imports)] + use std::{ + option::Option::{None, Some}, + result::Result::{Err, Ok}, + }; + roundtrip_full::("") +} + +#[test] +fn boxed_child_roundtrip_nested_2() { + #[allow(unused_imports)] + use std::{ + option::Option::{None, Some}, + result::Result::{Err, Ok}, + }; + roundtrip_full::("") +} diff --git a/xso/ChangeLog b/xso/ChangeLog index 16a564df..6b7ee3ed 100644 --- a/xso/ChangeLog +++ b/xso/ChangeLog @@ -14,8 +14,8 @@ Version NEXT: All this is to avoid triggering the camel case lint on the types we generate. * Added - - Support for child elements in derive macros. Child elements may be - wrapped in Option. + - Support for child elements in derive macros. Child elements may also + be wrapped in Option or Box. Version 0.1.2: 2024-07-26 Jonas Schäfer diff --git a/xso/src/lib.rs b/xso/src/lib.rs index f039eb58..5db231f7 100644 --- a/xso/src/lib.rs +++ b/xso/src/lib.rs @@ -94,6 +94,17 @@ impl<'x, T: Iterator, self::error::Error>>> Iterator for } } +/// Helper iterator to convert an `Box` to XML. +pub struct BoxAsXml(Box); + +impl<'x, T: Iterator, self::error::Error>>> Iterator for BoxAsXml { + type Item = Result, self::error::Error>; + + fn next(&mut self) -> Option { + self.0.next() + } +} + impl AsXml for Option { type ItemIter<'x> = OptionAsXml> where T: 'x; @@ -105,6 +116,14 @@ impl AsXml for Option { } } +impl AsXml for Box { + type ItemIter<'x> = BoxAsXml> where T: 'x; + + fn as_xml_iter(&self) -> Result, 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. /// @@ -134,6 +153,9 @@ pub trait FromEventsBuilder { /// Helper struct to construct an `Option` from XML events. pub struct OptionBuilder(T); +/// Helper struct to construct an `Box` from XML events. +pub struct BoxBuilder(Box); + impl FromEventsBuilder for OptionBuilder { type Output = Option; @@ -142,6 +164,14 @@ impl FromEventsBuilder for OptionBuilder { } } +impl FromEventsBuilder for BoxBuilder { + type Output = Box; + + fn feed(&mut self, ev: rxml::Event) -> Result, 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. /// @@ -190,6 +220,17 @@ impl FromXml for Option { } } +impl FromXml for Box { + type Builder = BoxBuilder; + + fn from_events( + name: rxml::QName, + attrs: rxml::AttrMap, + ) -> Result { + Ok(BoxBuilder(Box::new(T::from_events(name, attrs)?))) + } +} + /// Trait allowing to convert XML text to a value. /// /// This trait is similar to [`std::str::FromStr`], however, due to