From d623b6ab9bb349d4bbe6b976eac3e2581d033563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Wed, 3 Apr 2024 19:35:14 +0200 Subject: [PATCH] parsers-macros: wrap Extend trait into TryExtend for flexibility This allows containers to reject items based on their content, without having to resort to a panic. The use case is containers which are polymorphic, but don't support different item types, as encountered e.g. in XEP-0060 PubSub publish vs. retract events. --- parsers-core/src/lib.rs | 22 ++++++++++++++++++++++ parsers-macros/src/field/child.rs | 7 +++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/parsers-core/src/lib.rs b/parsers-core/src/lib.rs index 60e9b7af..76a8f8a1 100644 --- a/parsers-core/src/lib.rs +++ b/parsers-core/src/lib.rs @@ -800,3 +800,25 @@ impl ElementCodec for T { value } } + +/// Trait for fallible extension. +/// +/// You probably won't need to implement this---it is automatically implemented +/// for all containers which implement [`std::iter::Extend`]. +/// +/// The only useful case are containers where an extension may fail because of +/// data reasons, e.g. if you collect elements of multiple types but it can +/// only contain a single type at a time. +pub trait TryExtend { + /// Attempt extending the container with the given iterator. + /// + /// Upon error, the container may be partially extended. + fn try_extend>(&mut self, iter: T) -> Result<(), self::error::Error>; +} + +impl> TryExtend for T { + fn try_extend>(&mut self, iter: I) -> Result<(), self::error::Error> { + self.extend(iter); + Ok(()) + } +} diff --git a/parsers-macros/src/field/child.rs b/parsers-macros/src/field/child.rs index 7c929e64..ee8d6488 100644 --- a/parsers-macros/src/field/child.rs +++ b/parsers-macros/src/field/child.rs @@ -501,8 +501,7 @@ impl Field for ChildField { <#ty as IntoIterator>::Item }) .expect("failed to construct item type"); - let ty_extend = - quote_spanned! {ty_span=> <#ty as ::std::iter::Extend<#item_ty>>::extend}; + let ty_try_extend = quote_spanned! {ty_span=> <#ty as ::xmpp_parsers_core::TryExtend<#item_ty>>::try_extend}; match self.extract { Some(ref extract) => { let extract = extract.build_extract( @@ -517,7 +516,7 @@ impl Field for ChildField { childiter: quote! { residual = match #extract { Ok(v) => { - #ty_extend(&mut #tempname, [v]); + #ty_try_extend(&mut #tempname, [v])?; continue; }, Err(residual) => residual, @@ -552,7 +551,7 @@ impl Field for ChildField { let mut residual = match #codec_ty_from_tree(residual) { Ok(item) => { let item = #codec_ty_decode(item); - #ty_extend(&mut #tempname, [item]); + #ty_try_extend(&mut #tempname, [item])?; continue; }, Err(::xmpp_parsers_core::error::Error::TypeMismatch(_, _, e)) => e,