@@ -140,6 +140,26 @@ pub(crate) enum FieldIteratorPart {
},
}
+/// Definition of what to extract from a child element.
+struct ExtractDef {
+ /// The XML namespace of the child to extract data from.
+ xml_namespace: NamespaceRef,
+
+ /// The XML name of the child to extract data from.
+ xml_name: NameRef,
+
+ /// Compound which contains the arguments of the `extract(..)` meta
+ /// (except the `from`), transformed into a struct with unnamed
+ /// fields.
+ ///
+ /// This is used to generate the parsing/serialisation code, by
+ /// essentially "declaring" a shim struct, as if it were a real Rust
+ /// struct, and using the result of the parsing process directly for
+ /// the field on which the `extract(..)` option was used, instead of
+ /// putting it into a Rust struct.
+ parts: Compound,
+}
+
/// Specify how the field is mapped to XML.
enum FieldKind {
/// The field maps to an attribute.
@@ -169,26 +189,10 @@ enum FieldKind {
/// Number of child elements allowed.
amount: AmountConstraint,
- },
-
- /// Extract contents from a child element.
- Extract {
- /// The XML namespace of the child to extract data from.
- xml_namespace: NamespaceRef,
- /// The XML name of the child to extract data from.
- xml_name: NameRef,
-
- /// Compound which contains the arguments of the `extract(..)` meta
- /// (except the `from`), transformed into a struct with unnamed
- /// fields.
- ///
- /// This is used to generate the parsing/serialisation code, by
- /// essentially "declaring" a shim struct, as if it were a real Rust
- /// struct, and using the result of the parsing process directly for
- /// the field on which the `extract(..)` option was used, instead of
- /// putting it into a Rust struct.
- parts: Compound,
+ /// If set, the child element is not parsed as a field implementing
+ /// `FromXml` / `AsXml`, but instead its contents are extracted.
+ extract: Option<ExtractDef>,
},
}
@@ -269,6 +273,7 @@ impl FieldKind {
Ok(Self::Child {
default_,
amount: amount.unwrap_or(AmountConstraint::FixedSingle(Span::call_site())),
+ extract: None,
})
}
@@ -303,10 +308,14 @@ impl FieldKind {
[FieldDef::from_extract(field, 0, field_ty, &xml_namespace)].into_iter(),
)?;
- Ok(Self::Extract {
- xml_namespace,
- xml_name,
- parts,
+ Ok(Self::Child {
+ default_: Flag::Absent,
+ amount: AmountConstraint::FixedSingle(Span::call_site()),
+ extract: Some(ExtractDef {
+ xml_namespace,
+ xml_name,
+ parts,
+ }),
})
}
}
@@ -475,6 +484,7 @@ impl FieldDef {
FieldKind::Child {
ref default_,
ref amount,
+ ref extract,
} => {
let FromEventsScope {
ref substate_result,
@@ -487,11 +497,65 @@ impl FieldDef {
AmountConstraint::Any(_) => into_iterator_item_ty(self.ty.clone()),
};
- let from_events = from_events_fn(element_ty.clone());
- let from_xml_builder = from_xml_builder_ty(element_ty.clone());
+ let (extra_defs, matcher, fetch, builder) = match extract {
+ Some(ExtractDef {
+ ref xml_namespace,
+ ref xml_name,
+ ref parts,
+ }) => {
+ let from_xml_builder_ty_ident =
+ scope.make_member_type_name(&self.member, "FromXmlBuilder");
+ let state_ty_ident =
+ quote::format_ident!("{}State", from_xml_builder_ty_ident,);
+
+ let extra_defs = parts.make_from_events_statemachine(
+ &state_ty_ident,
+ &container_name.child(self.member.clone()),
+ "",
+ )?.with_augmented_init(|init| quote! {
+ if name.0 == #xml_namespace && name.1 == #xml_name {
+ #init
+ } else {
+ ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch { name, attrs })
+ }
+ }).compile().render(
+ &Visibility::Inherited,
+ &from_xml_builder_ty_ident,
+ &state_ty_ident,
+ &Type::Tuple(TypeTuple {
+ paren_token: token::Paren::default(),
+ elems: [
+ element_ty.clone(),
+ ].into_iter().collect(),
+ })
+ )?;
+ let from_xml_builder_ty =
+ ty_from_ident(from_xml_builder_ty_ident.clone()).into();
+
+ let matcher = quote! { #state_ty_ident::new(name, attrs).map(|x| #from_xml_builder_ty_ident(::core::option::Option::Some(x))) };
+
+ (
+ extra_defs,
+ matcher,
+ quote! { #substate_result.0 },
+ from_xml_builder_ty,
+ )
+ }
+ None => {
+ let from_events = from_events_fn(element_ty.clone());
+ let from_xml_builder = from_xml_builder_ty(element_ty.clone());
+
+ let matcher = quote! { #from_events(name, attrs) };
+ let builder = from_xml_builder;
- let matcher = quote! { #from_events(name, attrs) };
- let builder = from_xml_builder;
+ (
+ TokenStream::default(),
+ matcher,
+ quote! { #substate_result },
+ builder,
+ )
+ }
+ };
match amount {
AmountConstraint::FixedSingle(_) => {
@@ -505,7 +569,7 @@ impl FieldDef {
return ::core::result::Result::Err(::xso::error::Error::Other(#missing_msg).into())
},
Flag::Present(_) => {
- let default_ = default_fn(self.ty.clone());
+ let default_ = default_fn(element_ty.clone());
quote! {
#default_()
}
@@ -513,7 +577,7 @@ impl FieldDef {
};
Ok(FieldBuilderPart::Nested {
- extra_defs: TokenStream::default(),
+ extra_defs,
value: FieldTempInit {
init: quote! { ::std::option::Option::None },
ty: option_ty(self.ty.clone()),
@@ -530,7 +594,7 @@ impl FieldDef {
},
builder,
collect: quote! {
- #field_access = ::std::option::Option::Some(#substate_result);
+ #field_access = ::std::option::Option::Some(#fetch);
},
finalize: quote! {
match #field_access {
@@ -544,7 +608,7 @@ impl FieldDef {
let ty_extend = extend_fn(self.ty.clone(), element_ty.clone());
let ty_default = default_fn(self.ty.clone());
Ok(FieldBuilderPart::Nested {
- extra_defs: TokenStream::default(),
+ extra_defs,
value: FieldTempInit {
init: quote! { #ty_default() },
ty: self.ty.clone(),
@@ -552,87 +616,13 @@ impl FieldDef {
matcher,
builder,
collect: quote! {
- #ty_extend(&mut #field_access, [#substate_result]);
+ #ty_extend(&mut #field_access, [#fetch]);
},
finalize: quote! { #field_access },
})
}
}
}
-
- FieldKind::Extract {
- ref xml_namespace,
- ref xml_name,
- ref parts,
- } => {
- let FromEventsScope {
- ref substate_result,
- ..
- } = scope;
- let field_access = scope.access_field(&self.member);
-
- let missing_msg = error_message::on_missing_child(container_name, &self.member);
- let duplicate_msg = error_message::on_duplicate_child(container_name, &self.member);
-
- let on_absent = quote! {
- return ::core::result::Result::Err(::xso::error::Error::Other(#missing_msg).into())
- };
-
- let from_xml_builder_ty_ident =
- scope.make_member_type_name(&self.member, "FromXmlBuilder");
- let state_ty_ident = quote::format_ident!("{}State", from_xml_builder_ty_ident,);
-
- let extra_defs = parts.make_from_events_statemachine(
- &state_ty_ident,
- &container_name.child(self.member.clone()),
- "",
- )?.with_augmented_init(|init| quote! {
- if name.0 == #xml_namespace && name.1 == #xml_name {
- #init
- } else {
- ::core::result::Result::Err(::xso::error::FromEventsError::Mismatch { name, attrs })
- }
- }).compile().render(
- &Visibility::Inherited,
- &from_xml_builder_ty_ident,
- &state_ty_ident,
- &Type::Tuple(TypeTuple {
- paren_token: token::Paren::default(),
- elems: [
- self.ty.clone(),
- ].into_iter().collect(),
- })
- )?;
- let from_xml_builder_ty = ty_from_ident(from_xml_builder_ty_ident.clone()).into();
-
- Ok(FieldBuilderPart::Nested {
- extra_defs,
- value: FieldTempInit {
- init: quote! { ::std::option::Option::None },
- ty: option_ty(self.ty.clone()),
- },
- matcher: quote! {
- match #state_ty_ident::new(name, attrs) {
- ::core::result::Result::Ok(v) => if #field_access.is_some() {
- ::core::result::Result::Err(::xso::error::FromEventsError::Invalid(::xso::error::Error::Other(#duplicate_msg)))
- } else {
- ::core::result::Result::Ok(#from_xml_builder_ty_ident(::core::option::Option::Some(v)))
- },
- ::core::result::Result::Err(e) => ::core::result::Result::Err(e),
- }
- },
- builder: from_xml_builder_ty,
- collect: quote! {
- #field_access = ::std::option::Option::Some(#substate_result.0);
- },
- finalize: quote! {
- match #field_access {
- ::std::option::Option::Some(value) => value,
- ::std::option::Option::None => #on_absent,
- }
- },
- })
- }
}
}
@@ -689,147 +679,146 @@ impl FieldDef {
FieldKind::Child {
default_: _,
- amount: AmountConstraint::FixedSingle(_),
- } => {
- let AsItemsScope { ref lifetime, .. } = scope;
-
- let as_xml_iter = as_xml_iter_fn(self.ty.clone());
- let item_iter = item_iter_ty(self.ty.clone(), lifetime.clone());
-
- Ok(FieldIteratorPart::Content {
- extra_defs: TokenStream::default(),
- value: FieldTempInit {
- init: quote! {
- #as_xml_iter(#bound_name)?
- },
- ty: item_iter,
- },
- generator: quote! {
- #bound_name.next().transpose()
- },
- })
- }
-
- FieldKind::Child {
- default_: _,
- amount: AmountConstraint::Any(_),
+ ref amount,
+ ref extract,
} => {
let AsItemsScope { ref lifetime, .. } = scope;
- // This should give us the type of element stored in the
- // collection.
- let element_ty = into_iterator_item_ty(self.ty.clone());
-
- // And this is the collection type we actually work with --
- // as_xml_iter uses references after all.
- let ty = ref_ty(self.ty.clone(), lifetime.clone());
+ let item_ty = match amount {
+ AmountConstraint::FixedSingle(_) => self.ty.clone(),
+ AmountConstraint::Any(_) => {
+ // This should give us the type of element stored in the
+ // collection.
+ into_iterator_item_ty(self.ty.clone())
+ }
+ };
- // as_xml_iter is called on the bare type (not the ref type)
- let as_xml_iter = as_xml_iter_fn(element_ty.clone());
+ let (extra_defs, fetch, as_xml_iter, iter_ty) = match extract {
+ Some(ExtractDef {
+ ref xml_namespace,
+ ref xml_name,
+ ref parts,
+ }) => {
+ let item_iter_ty_ident =
+ scope.make_member_type_name(&self.member, "AsXmlIterator");
+ let state_ty_ident = quote::format_ident!("{}State", item_iter_ty_ident,);
+ let mut item_iter_ty = ty_from_ident(item_iter_ty_ident.clone());
+ item_iter_ty.path.segments[0].arguments =
+ PathArguments::AngleBracketed(AngleBracketedGenericArguments {
+ colon2_token: None,
+ lt_token: token::Lt::default(),
+ args: [GenericArgument::Lifetime(lifetime.clone())]
+ .into_iter()
+ .collect(),
+ gt_token: token::Gt::default(),
+ });
+ let item_iter_ty = item_iter_ty.into();
+
+ let extra_defs = parts
+ .make_as_item_iter_statemachine(
+ &container_name.child(self.member.clone()),
+ &state_ty_ident,
+ "",
+ lifetime,
+ )?
+ .with_augmented_init(|init| {
+ quote! {
+ let name = (
+ ::xso::exports::rxml::Namespace::from(#xml_namespace),
+ ::std::borrow::Cow::Borrowed(#xml_name),
+ );
+ #init
+ }
+ })
+ .compile()
+ .render(
+ &Visibility::Inherited,
+ &Type::Tuple(TypeTuple {
+ paren_token: token::Paren::default(),
+ elems: [ref_ty(item_ty.clone(), lifetime.clone())]
+ .into_iter()
+ .collect(),
+ }),
+ &state_ty_ident,
+ lifetime,
+ &item_iter_ty,
+ )?;
+
+ (
+ extra_defs,
+ quote! { (&#bound_name,) },
+ quote! { #item_iter_ty_ident::new },
+ item_iter_ty,
+ )
+ }
+ None => {
+ let as_xml_iter = as_xml_iter_fn(item_ty.clone());
+ let item_iter = item_iter_ty(item_ty.clone(), lifetime.clone());
+
+ (
+ TokenStream::default(),
+ quote! { #bound_name },
+ quote! { #as_xml_iter },
+ item_iter,
+ )
+ }
+ };
- // And thus the iterator associated with AsXml is also derived
- // from the bare type.
- let item_iter = item_iter_ty(element_ty.clone(), lifetime.clone());
+ match amount {
+ AmountConstraint::FixedSingle(_) => Ok(FieldIteratorPart::Content {
+ extra_defs,
+ value: FieldTempInit {
+ init: quote! {
+ #as_xml_iter(#fetch)?
+ },
+ ty: iter_ty,
+ },
+ generator: quote! {
+ #bound_name.next().transpose()
+ },
+ }),
+ AmountConstraint::Any(_) => {
+ // This is the collection type we actually work
+ // with -- as_xml_iter uses references after all.
+ let ty = ref_ty(self.ty.clone(), lifetime.clone());
- // But the iterator for iterating over the elements inside the
- // collection must use the ref type.
- let element_iter = into_iterator_iter_ty(ty.clone());
+ // But the iterator for iterating over the elements
+ // inside the collection must use the ref type.
+ let element_iter = into_iterator_iter_ty(ty.clone());
- // And likewise the into_iter impl.
- let into_iter = into_iterator_into_iter_fn(ty.clone());
+ // And likewise the into_iter impl.
+ let into_iter = into_iterator_into_iter_fn(ty.clone());
- let state_ty = Type::Tuple(TypeTuple {
- paren_token: token::Paren::default(),
- elems: [element_iter, option_ty(item_iter)].into_iter().collect(),
- });
+ let state_ty = Type::Tuple(TypeTuple {
+ paren_token: token::Paren::default(),
+ elems: [element_iter, option_ty(iter_ty)].into_iter().collect(),
+ });
- Ok(FieldIteratorPart::Content {
- extra_defs: TokenStream::default(),
- value: FieldTempInit {
- init: quote! {
- (#into_iter(#bound_name), ::core::option::Option::None)
- },
- ty: state_ty,
- },
- generator: quote! {
- loop {
- if let ::core::option::Option::Some(current) = #bound_name.1.as_mut() {
- if let ::core::option::Option::Some(item) = current.next() {
- break ::core::option::Option::Some(item).transpose();
+ Ok(FieldIteratorPart::Content {
+ extra_defs,
+ value: FieldTempInit {
+ init: quote! {
+ (#into_iter(#bound_name), ::core::option::Option::None)
+ },
+ ty: state_ty,
+ },
+ generator: quote! {
+ loop {
+ if let ::core::option::Option::Some(current) = #bound_name.1.as_mut() {
+ if let ::core::option::Option::Some(item) = current.next() {
+ break ::core::option::Option::Some(item).transpose();
+ }
+ }
+ if let ::core::option::Option::Some(item) = #bound_name.0.next() {
+ #bound_name.1 = ::core::option::Option::Some(#as_xml_iter({ let #bound_name = item; #fetch })?)
+ } else {
+ break ::core::result::Result::Ok(::core::option::Option::None)
+ }
}
- }
- if let ::core::option::Option::Some(item) = #bound_name.0.next() {
- #bound_name.1 = ::core::option::Option::Some(#as_xml_iter(item)?)
- } else {
- break ::core::result::Result::Ok(::core::option::Option::None)
- }
- }
- },
- })
- }
-
- FieldKind::Extract {
- ref xml_namespace,
- ref xml_name,
- ref parts,
- } => {
- let AsItemsScope { ref lifetime, .. } = scope;
- let item_iter_ty_ident = scope.make_member_type_name(&self.member, "AsXmlIterator");
- let state_ty_ident = quote::format_ident!("{}State", item_iter_ty_ident,);
- let mut item_iter_ty = ty_from_ident(item_iter_ty_ident.clone());
- item_iter_ty.path.segments[0].arguments =
- PathArguments::AngleBracketed(AngleBracketedGenericArguments {
- colon2_token: None,
- lt_token: token::Lt::default(),
- args: [GenericArgument::Lifetime(lifetime.clone())]
- .into_iter()
- .collect(),
- gt_token: token::Gt::default(),
- });
- let item_iter_ty = item_iter_ty.into();
-
- let extra_defs = parts
- .make_as_item_iter_statemachine(
- &container_name.child(self.member.clone()),
- &state_ty_ident,
- "",
- lifetime,
- )?
- .with_augmented_init(|init| {
- quote! {
- let name = (
- ::xso::exports::rxml::Namespace::from(#xml_namespace),
- ::std::borrow::Cow::Borrowed(#xml_name),
- );
- #init
- }
- })
- .compile()
- .render(
- &Visibility::Inherited,
- &Type::Tuple(TypeTuple {
- paren_token: token::Paren::default(),
- elems: [ref_ty(self.ty.clone(), lifetime.clone())]
- .into_iter()
- .collect(),
- }),
- &state_ty_ident,
- lifetime,
- &item_iter_ty,
- )?;
-
- Ok(FieldIteratorPart::Content {
- extra_defs,
- value: FieldTempInit {
- init: quote! {
- #item_iter_ty_ident::new((&#bound_name,))?
- },
- ty: item_iter_ty,
- },
- generator: quote! {
- #bound_name.next().transpose()
- },
- })
+ },
+ })
+ }
+ }
}
}
}