diff --git a/xso-proc/src/compound.rs b/xso-proc/src/compound.rs index adfe989029b51ecb93236dd39d4c62f60197ea20..6287a340b580d75c3d65c88ab2728b6d5f837b8f 100644 --- a/xso-proc/src/compound.rs +++ b/xso-proc/src/compound.rs @@ -572,6 +572,15 @@ impl Compound { } } + /// Construct a tuple type with this compound's field's types in the same + /// order as they appear in the compound. + pub(crate) fn to_single_or_tuple_ty(&self) -> Type { + match self.single_ty() { + None => self.to_tuple_ty().into(), + Some(v) => v.clone(), + } + } + /// Construct a tuple type with references to this compound's field's /// types in the same order as they appear in the compound, with the given /// lifetime. diff --git a/xso-proc/src/field/child.rs b/xso-proc/src/field/child.rs index 062d981108e898af67d80fbbc3ede4d172e20ed4..8db8072b32d800c1a5a6e01301f9764ecefc8adf 100644 --- a/xso-proc/src/field/child.rs +++ b/xso-proc/src/field/child.rs @@ -10,8 +10,8 @@ //! implementations in a single type. use proc_macro2::{Span, TokenStream}; -use quote::quote; -use syn::*; +use quote::{quote, quote_spanned}; +use syn::{spanned::Spanned, *}; use crate::compound::Compound; use crate::error_message::{self, ParentRef}; @@ -53,9 +53,13 @@ impl Field for ChildField { }; let (extra_defs, matcher, fetch, builder) = match self.extract { - Some(ref extract) => { - extract.make_from_xml_builder_parts(scope, container_name, member, is_container)? - } + Some(ref extract) => extract.make_from_xml_builder_parts( + scope, + container_name, + member, + is_container, + ty, + )?, None => { let FromEventsScope { ref substate_result, @@ -165,6 +169,7 @@ impl Field for ChildField { let (extra_defs, init, iter_ty) = match self.extract { Some(ref extract) => extract.make_as_item_iter_parts( scope, + ty, container_name, bound_name, member, @@ -271,6 +276,7 @@ impl ExtractDef { container_name: &ParentRef, member: &Member, collecting_into_container: bool, + output_ty: &Type, ) -> Result<(TokenStream, TokenStream, TokenStream, Type)> { let FromEventsScope { ref substate_result, @@ -303,6 +309,8 @@ impl ExtractDef { let matcher = quote! { #state_ty_ident::new(name, attrs).map(|x| #from_xml_builder_ty_ident(::core::option::Option::Some(x))) }; + let inner_ty = self.parts.to_single_or_tuple_ty(); + let fetch = if self.parts.field_count() == 1 { // If we extract only a single field, we automatically unwrap the // tuple, because that behaviour is more obvious to users. @@ -337,8 +345,9 @@ impl ExtractDef { // the type constraint imposed by the place the value is *used*, // which is strictly bound by the field's type (so there is, in // fact, no ambiguity). So this works all kinds of magic. - quote! { - #fetch.into() + quote_spanned! { + output_ty.span()=> + <#output_ty as ::core::convert::From::<#inner_ty>>::from(#fetch) } }; @@ -352,6 +361,7 @@ impl ExtractDef { fn make_as_item_iter_parts( &self, scope: &AsItemsScope, + input_ty: &Type, container_name: &ParentRef, bound_name: &Ident, member: &Member, @@ -450,9 +460,12 @@ impl ExtractDef { // about that in [`make_from_xml_builder_parts`] implementation // corresponding to this code above, and we will not repeat it // here. + let cast = quote_spanned! { input_ty.span()=> + ::core::option::Option::from(#bound_name) + }; ( quote! { - ::xso::asxml::OptionAsXml::new(::core::option::Option::from(#bound_name).map(|#bound_name: #inner_ty| { + ::xso::asxml::OptionAsXml::new(#cast.map(|#bound_name: #inner_ty| { #item_iter_ty_ident::new((#repack)) }).transpose()?) },