xso_proc: 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.
This commit is contained in:
Jonas Schäfer 2024-04-03 19:35:14 +02:00
parent a60542af91
commit 6054063bd8
2 changed files with 26 additions and 4 deletions

View File

@ -502,8 +502,8 @@ 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 ::xso::TryExtend<#item_ty>>::try_extend};
match self.extract {
Some(ref extract) => {
let extract = extract.build_extract(
@ -518,7 +518,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,
@ -553,7 +553,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(::xso::error::Error::TypeMismatch(_, _, e)) => e,

View File

@ -800,3 +800,25 @@ impl<T> ElementCodec<T> 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<A> {
/// Attempt extending the container with the given iterator.
///
/// Upon error, the container may be partially extended.
fn try_extend<T: IntoIterator<Item = A>>(&mut self, iter: T) -> Result<(), self::error::Error>;
}
impl<A, T: Extend<A>> TryExtend<A> for T {
fn try_extend<I: IntoIterator<Item = A>>(&mut self, iter: I) -> Result<(), self::error::Error> {
self.extend(iter);
Ok(())
}
}