xso-proc: add support for child elements
This commit is contained in:
parent
1265f4bb67
commit
5bd36eccfc
11 changed files with 576 additions and 13 deletions
|
|
@ -14,9 +14,10 @@ use rxml_validation::NcName;
|
|||
|
||||
use crate::error_message::{self, ParentRef};
|
||||
use crate::meta::{Flag, NameRef, NamespaceRef, XmlFieldMeta};
|
||||
use crate::scope::FromEventsScope;
|
||||
use crate::scope::{AsItemsScope, FromEventsScope};
|
||||
use crate::types::{
|
||||
as_optional_xml_text_fn, as_xml_text_fn, default_fn, from_xml_text_fn, string_ty,
|
||||
as_optional_xml_text_fn, as_xml_iter_fn, as_xml_text_fn, default_fn, from_events_fn,
|
||||
from_xml_builder_ty, from_xml_text_fn, item_iter_ty, option_ty, string_ty,
|
||||
text_codec_decode_fn, text_codec_encode_fn,
|
||||
};
|
||||
|
||||
|
|
@ -58,6 +59,38 @@ pub(crate) enum FieldBuilderPart {
|
|||
/// temporary value.
|
||||
finalize: TokenStream,
|
||||
},
|
||||
|
||||
/// Parse a field from child element events.
|
||||
Nested {
|
||||
/// Expression and type which initializes a buffer to use during
|
||||
/// parsing.
|
||||
value: FieldTempInit,
|
||||
|
||||
/// Expression which evaluates to `Result<T, FromEventsError>`,
|
||||
/// consuming `name: rxml::QName` and `attrs: rxml::AttrMap`.
|
||||
///
|
||||
/// `T` must be the type specified in the
|
||||
/// [`Self::Nested::builder`] field.
|
||||
matcher: TokenStream,
|
||||
|
||||
/// Type implementing `xso::FromEventsBuilder` which parses the child
|
||||
/// element.
|
||||
///
|
||||
/// This type is returned by the expressions in
|
||||
/// [`matcher`][`Self::Nested::matcher`].
|
||||
builder: Type,
|
||||
|
||||
/// Expression which consumes the value stored in the identifier
|
||||
/// [`crate::common::FromEventsScope::substate_result`][`FromEventsScope::substate_result`]
|
||||
/// and somehow collects it into the field declared with
|
||||
/// [`value`][`Self::Nested::value`].
|
||||
collect: TokenStream,
|
||||
|
||||
/// Expression which consumes the data from the field declared with
|
||||
/// [`value`][`Self::Nested::value`] and converts it into the field's
|
||||
/// type.
|
||||
finalize: TokenStream,
|
||||
},
|
||||
}
|
||||
|
||||
/// Describe how a struct or enum variant's member is converted to XML data.
|
||||
|
|
@ -80,6 +113,21 @@ pub(crate) enum FieldIteratorPart {
|
|||
/// String, which is then emitted as text data.
|
||||
generator: TokenStream,
|
||||
},
|
||||
|
||||
/// The field is emitted as series of items which form a child element.
|
||||
Content {
|
||||
/// Expression and type which initializes the nested iterator.
|
||||
///
|
||||
/// Note that this is evaluated at construction time of the iterator.
|
||||
/// Fields of this variant do not get access to their original data,
|
||||
/// unless they carry it in the contents of this `value`.
|
||||
value: FieldTempInit,
|
||||
|
||||
/// An expression which uses the value (mutably) and evaluates to
|
||||
/// a Result<Option<Item>, Error>. Once the state returns None, the
|
||||
/// processing will advance to the next state.
|
||||
generator: TokenStream,
|
||||
},
|
||||
}
|
||||
|
||||
/// Specify how the field is mapped to XML.
|
||||
|
|
@ -102,6 +150,9 @@ enum FieldKind {
|
|||
/// Optional codec to use
|
||||
codec: Option<Type>,
|
||||
},
|
||||
|
||||
/// The field maps to a child
|
||||
Child,
|
||||
}
|
||||
|
||||
impl FieldKind {
|
||||
|
|
@ -147,6 +198,8 @@ impl FieldKind {
|
|||
}
|
||||
|
||||
XmlFieldMeta::Text { codec } => Ok(Self::Text { codec }),
|
||||
|
||||
XmlFieldMeta::Child => Ok(Self::Child),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -287,6 +340,39 @@ impl FieldDef {
|
|||
finalize,
|
||||
})
|
||||
}
|
||||
|
||||
FieldKind::Child => {
|
||||
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 from_events = from_events_fn(self.ty.clone());
|
||||
let from_xml_builder = from_xml_builder_ty(self.ty.clone());
|
||||
|
||||
Ok(FieldBuilderPart::Nested {
|
||||
value: FieldTempInit {
|
||||
init: quote! { ::std::option::Option::None },
|
||||
ty: option_ty(self.ty.clone()),
|
||||
},
|
||||
matcher: quote! {
|
||||
#from_events(name, attrs)
|
||||
},
|
||||
builder: from_xml_builder,
|
||||
collect: quote! {
|
||||
#field_access = ::std::option::Option::Some(#substate_result);
|
||||
},
|
||||
finalize: quote! {
|
||||
match #field_access {
|
||||
::std::option::Option::Some(value) => value,
|
||||
::std::option::Option::None => return ::core::result::Result::Err(::xso::error::Error::Other(#missing_msg).into()),
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -294,7 +380,11 @@ impl FieldDef {
|
|||
///
|
||||
/// `bound_name` must be the name to which the field's value is bound in
|
||||
/// the iterator code.
|
||||
pub(crate) fn make_iterator_part(&self, bound_name: &Ident) -> Result<FieldIteratorPart> {
|
||||
pub(crate) fn make_iterator_part(
|
||||
&self,
|
||||
scope: &AsItemsScope,
|
||||
bound_name: &Ident,
|
||||
) -> Result<FieldIteratorPart> {
|
||||
match self.kind {
|
||||
FieldKind::Attribute {
|
||||
ref xml_name,
|
||||
|
|
@ -335,6 +425,25 @@ impl FieldDef {
|
|||
|
||||
Ok(FieldIteratorPart::Text { generator })
|
||||
}
|
||||
|
||||
FieldKind::Child => {
|
||||
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 {
|
||||
value: FieldTempInit {
|
||||
init: quote! {
|
||||
#as_xml_iter(#bound_name)?
|
||||
},
|
||||
ty: item_iter,
|
||||
},
|
||||
generator: quote! {
|
||||
#bound_name.next().transpose()
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue