Port everything over to AsXml
This commit is contained in:
parent
4910b01244
commit
ccf38cdf9b
64 changed files with 848 additions and 371 deletions
|
|
@ -12,9 +12,9 @@ use syn::{spanned::Spanned, *};
|
|||
|
||||
use crate::error_message::ParentRef;
|
||||
use crate::field::{FieldBuilderPart, FieldDef, FieldIteratorPart, FieldTempInit};
|
||||
use crate::scope::{mangle_member, FromEventsScope, IntoEventsScope};
|
||||
use crate::state::{FromEventsSubmachine, IntoEventsSubmachine, State};
|
||||
use crate::types::qname_ty;
|
||||
use crate::scope::{mangle_member, AsItemsScope, FromEventsScope};
|
||||
use crate::state::{AsItemsSubmachine, FromEventsSubmachine, State};
|
||||
use crate::types::{namespace_ty, ncnamestr_cow_ty, phantom_lifetime_ty};
|
||||
|
||||
/// A struct or enum variant's contents.
|
||||
pub(crate) struct Compound {
|
||||
|
|
@ -230,58 +230,86 @@ impl Compound {
|
|||
/// **Important:** The returned submachine is not in functional state!
|
||||
/// It's `init` must be modified so that a variable called `name` of type
|
||||
/// `rxml::QName` is in scope.
|
||||
pub(crate) fn make_into_event_iter_statemachine(
|
||||
pub(crate) fn make_as_item_iter_statemachine(
|
||||
&self,
|
||||
input_name: &Path,
|
||||
state_prefix: &str,
|
||||
) -> Result<IntoEventsSubmachine> {
|
||||
let scope = IntoEventsScope::new();
|
||||
let IntoEventsScope { ref attrs, .. } = scope;
|
||||
lifetime: &Lifetime,
|
||||
) -> Result<AsItemsSubmachine> {
|
||||
let scope = AsItemsScope::new(lifetime);
|
||||
|
||||
let start_element_state_ident = quote::format_ident!("{}StartElement", state_prefix);
|
||||
let end_element_state_ident = quote::format_ident!("{}EndElement", state_prefix);
|
||||
let element_head_start_state_ident =
|
||||
quote::format_ident!("{}ElementHeadStart", state_prefix);
|
||||
let element_head_end_state_ident = quote::format_ident!("{}ElementHeadEnd", state_prefix);
|
||||
let element_foot_state_ident = quote::format_ident!("{}ElementFoot", state_prefix);
|
||||
let name_ident = quote::format_ident!("name");
|
||||
let ns_ident = quote::format_ident!("ns");
|
||||
let dummy_ident = quote::format_ident!("dummy");
|
||||
let mut states = Vec::new();
|
||||
|
||||
let mut init_body = TokenStream::default();
|
||||
let mut destructure = TokenStream::default();
|
||||
let mut start_init = TokenStream::default();
|
||||
|
||||
states.push(
|
||||
State::new(start_element_state_ident.clone())
|
||||
.with_field(&name_ident, &qname_ty(Span::call_site())),
|
||||
State::new(element_head_start_state_ident.clone())
|
||||
.with_field(&dummy_ident, &phantom_lifetime_ty(lifetime.clone()))
|
||||
.with_field(&ns_ident, &namespace_ty(Span::call_site()))
|
||||
.with_field(
|
||||
&name_ident,
|
||||
&ncnamestr_cow_ty(Span::call_site(), lifetime.clone()),
|
||||
),
|
||||
);
|
||||
|
||||
let mut element_head_end_idx = states.len();
|
||||
states.push(
|
||||
State::new(element_head_end_state_ident.clone()).with_impl(quote! {
|
||||
::core::option::Option::Some(::xso::Item::ElementHeadEnd)
|
||||
}),
|
||||
);
|
||||
|
||||
for (i, field) in self.fields.iter().enumerate() {
|
||||
let member = field.member();
|
||||
let bound_name = mangle_member(member);
|
||||
let part = field.make_iterator_part(&scope, &bound_name)?;
|
||||
let part = field.make_iterator_part(&bound_name)?;
|
||||
let state_name = quote::format_ident!("{}Field{}", state_prefix, i);
|
||||
let ty = scope.borrow(field.ty().clone());
|
||||
|
||||
match part {
|
||||
FieldIteratorPart::Header { setter } => {
|
||||
FieldIteratorPart::Header { generator } => {
|
||||
// we have to make sure that we carry our data around in
|
||||
// all the previous states.
|
||||
for state in &mut states[..element_head_end_idx] {
|
||||
state.add_field(&bound_name, &ty);
|
||||
}
|
||||
states.insert(
|
||||
element_head_end_idx,
|
||||
State::new(state_name)
|
||||
.with_field(&bound_name, &ty)
|
||||
.with_impl(quote! {
|
||||
#generator
|
||||
}),
|
||||
);
|
||||
element_head_end_idx += 1;
|
||||
|
||||
destructure.extend(quote! {
|
||||
#member: #bound_name,
|
||||
#member: ref #bound_name,
|
||||
});
|
||||
init_body.extend(setter);
|
||||
start_init.extend(quote! {
|
||||
#bound_name,
|
||||
});
|
||||
states[0].add_field(&bound_name, field.ty());
|
||||
}
|
||||
|
||||
FieldIteratorPart::Text { generator } => {
|
||||
// we have to make sure that we carry our data around in
|
||||
// all the previous states.
|
||||
for state in states.iter_mut() {
|
||||
state.add_field(&bound_name, field.ty());
|
||||
state.add_field(&bound_name, &ty);
|
||||
}
|
||||
states.push(
|
||||
State::new(state_name)
|
||||
.with_field(&bound_name, field.ty())
|
||||
.with_field(&bound_name, &ty)
|
||||
.with_impl(quote! {
|
||||
#generator.map(|value| ::xso::exports::rxml::Event::Text(
|
||||
::xso::exports::rxml::parser::EventMetrics::zero(),
|
||||
#generator.map(|value| ::xso::Item::Text(
|
||||
value,
|
||||
))
|
||||
}),
|
||||
|
|
@ -298,32 +326,27 @@ impl Compound {
|
|||
|
||||
states[0].set_impl(quote! {
|
||||
{
|
||||
let mut #attrs = ::xso::exports::rxml::AttrMap::new();
|
||||
#init_body
|
||||
::core::option::Option::Some(::xso::exports::rxml::Event::StartElement(
|
||||
::xso::exports::rxml::parser::EventMetrics::zero(),
|
||||
::core::option::Option::Some(::xso::Item::ElementHeadStart(
|
||||
#ns_ident,
|
||||
#name_ident,
|
||||
#attrs,
|
||||
))
|
||||
}
|
||||
});
|
||||
|
||||
states.push(
|
||||
State::new(end_element_state_ident.clone()).with_impl(quote! {
|
||||
::core::option::Option::Some(::xso::exports::rxml::Event::EndElement(
|
||||
::xso::exports::rxml::parser::EventMetrics::zero(),
|
||||
))
|
||||
State::new(element_foot_state_ident.clone()).with_impl(quote! {
|
||||
::core::option::Option::Some(::xso::Item::ElementFoot)
|
||||
}),
|
||||
);
|
||||
|
||||
Ok(IntoEventsSubmachine {
|
||||
Ok(AsItemsSubmachine {
|
||||
defs: TokenStream::default(),
|
||||
states,
|
||||
destructure: quote! {
|
||||
#input_name { #destructure }
|
||||
},
|
||||
init: quote! {
|
||||
Self::#start_element_state_ident { #name_ident, #start_init }
|
||||
Self::#element_head_start_state_ident { #dummy_ident: ::std::marker::PhantomData, #name_ident: name.1, #ns_ident: name.0, #start_init }
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ use rxml_validation::NcName;
|
|||
|
||||
use crate::error_message::{self, ParentRef};
|
||||
use crate::meta::{Flag, NameRef, NamespaceRef, XmlFieldMeta};
|
||||
use crate::scope::{FromEventsScope, IntoEventsScope};
|
||||
use crate::scope::FromEventsScope;
|
||||
use crate::types::{
|
||||
default_fn, from_xml_text_fn, into_optional_xml_text_fn, into_xml_text_fn, string_ty,
|
||||
as_optional_xml_text_fn, as_xml_text_fn, default_fn, from_xml_text_fn, string_ty,
|
||||
text_codec_decode_fn, text_codec_encode_fn,
|
||||
};
|
||||
|
||||
|
|
@ -69,13 +69,12 @@ pub(crate) enum FieldBuilderPart {
|
|||
pub(crate) enum FieldIteratorPart {
|
||||
/// The field is emitted as part of StartElement.
|
||||
Header {
|
||||
/// A sequence of statements which updates the temporary variables
|
||||
/// during the StartElement event's construction, consuming the
|
||||
/// field's value.
|
||||
setter: TokenStream,
|
||||
/// An expression which consumes the field's value and returns a
|
||||
/// `Item`.
|
||||
generator: TokenStream,
|
||||
},
|
||||
|
||||
/// The field is emitted as text event.
|
||||
/// The field is emitted as text item.
|
||||
Text {
|
||||
/// An expression which consumes the field's value and returns a
|
||||
/// String, which is then emitted as text data.
|
||||
|
|
@ -295,19 +294,13 @@ 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,
|
||||
scope: &IntoEventsScope,
|
||||
bound_name: &Ident,
|
||||
) -> Result<FieldIteratorPart> {
|
||||
pub(crate) fn make_iterator_part(&self, bound_name: &Ident) -> Result<FieldIteratorPart> {
|
||||
match self.kind {
|
||||
FieldKind::Attribute {
|
||||
ref xml_name,
|
||||
ref xml_namespace,
|
||||
..
|
||||
} => {
|
||||
let IntoEventsScope { ref attrs, .. } = scope;
|
||||
|
||||
let xml_namespace = match xml_namespace {
|
||||
Some(v) => quote! { ::xso::exports::rxml::Namespace::from(#v) },
|
||||
None => quote! {
|
||||
|
|
@ -315,16 +308,13 @@ impl FieldDef {
|
|||
},
|
||||
};
|
||||
|
||||
let into_optional_xml_text = into_optional_xml_text_fn(self.ty.clone());
|
||||
let as_optional_xml_text = as_optional_xml_text_fn(self.ty.clone());
|
||||
|
||||
Ok(FieldIteratorPart::Header {
|
||||
// This is a neat little trick:
|
||||
// Option::from(x) converts x to an Option<T> *unless* it
|
||||
// already is an Option<_>.
|
||||
setter: quote! {
|
||||
#into_optional_xml_text(#bound_name)?.and_then(|#bound_name| #attrs.insert(
|
||||
generator: quote! {
|
||||
#as_optional_xml_text(#bound_name)?.map(|#bound_name| ::xso::Item::Attribute(
|
||||
#xml_namespace,
|
||||
#xml_name.to_owned(),
|
||||
::std::borrow::Cow::Borrowed(#xml_name),
|
||||
#bound_name,
|
||||
));
|
||||
},
|
||||
|
|
@ -338,8 +328,8 @@ impl FieldDef {
|
|||
quote! { #encode(#bound_name)? }
|
||||
}
|
||||
None => {
|
||||
let into_xml_text = into_xml_text_fn(self.ty.clone());
|
||||
quote! { ::core::option::Option::Some(#into_xml_text(#bound_name)?) }
|
||||
let as_xml_text = as_xml_text_fn(self.ty.clone());
|
||||
quote! { ::core::option::Option::Some(#as_xml_text(#bound_name)?) }
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -111,26 +111,27 @@ pub fn from_xml(input: RawTokenStream) -> RawTokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
/// Generate a `xso::IntoXml` implementation for the given item, or fail with
|
||||
/// Generate a `xso::AsXml` implementation for the given item, or fail with
|
||||
/// a proper compiler error.
|
||||
fn into_xml_impl(input: Item) -> Result<TokenStream> {
|
||||
fn as_xml_impl(input: Item) -> Result<TokenStream> {
|
||||
let (vis, ident, def) = parse_struct(input)?;
|
||||
|
||||
let structs::IntoXmlParts {
|
||||
let structs::AsXmlParts {
|
||||
defs,
|
||||
into_event_iter_body,
|
||||
event_iter_ty_ident,
|
||||
} = def.make_into_event_iter(&vis)?;
|
||||
as_xml_iter_body,
|
||||
item_iter_ty_lifetime,
|
||||
item_iter_ty,
|
||||
} = def.make_as_xml_iter(&vis)?;
|
||||
|
||||
#[cfg_attr(not(feature = "minidom"), allow(unused_mut))]
|
||||
let mut result = quote! {
|
||||
#defs
|
||||
|
||||
impl ::xso::IntoXml for #ident {
|
||||
type EventIter = #event_iter_ty_ident;
|
||||
impl ::xso::AsXml for #ident {
|
||||
type ItemIter<#item_iter_ty_lifetime> = #item_iter_ty;
|
||||
|
||||
fn into_event_iter(self) -> ::core::result::Result<Self::EventIter, ::xso::error::Error> {
|
||||
#into_event_iter_body
|
||||
fn as_xml_iter(&self) -> ::core::result::Result<Self::ItemIter<'_>, ::xso::error::Error> {
|
||||
#as_xml_iter_body
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -162,15 +163,15 @@ fn into_xml_impl(input: Item) -> Result<TokenStream> {
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
/// Macro to derive a `xso::IntoXml` implementation on a type.
|
||||
/// Macro to derive a `xso::AsXml` implementation on a type.
|
||||
///
|
||||
/// The user-facing documentation for this macro lives in the `xso` crate.
|
||||
#[proc_macro_derive(IntoXml, attributes(xml))]
|
||||
pub fn into_xml(input: RawTokenStream) -> RawTokenStream {
|
||||
// Shim wrapper around `into_xml_impl` which converts any errors into
|
||||
#[proc_macro_derive(AsXml, attributes(xml))]
|
||||
pub fn as_xml(input: RawTokenStream) -> RawTokenStream {
|
||||
// Shim wrapper around `as_xml_impl` which converts any errors into
|
||||
// actual compiler errors within the resulting token stream.
|
||||
let item = syn::parse_macro_input!(input as Item);
|
||||
match into_xml_impl(item) {
|
||||
match as_xml_impl(item) {
|
||||
Ok(v) => v.into(),
|
||||
Err(e) => e.into_compile_error().into(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
use proc_macro2::Span;
|
||||
use syn::*;
|
||||
|
||||
use crate::types::ref_ty;
|
||||
|
||||
/// Container struct for various identifiers used throughout the parser code.
|
||||
///
|
||||
/// This struct is passed around from the [`crate::compound::Compound`]
|
||||
|
|
@ -80,19 +82,24 @@ impl FromEventsScope {
|
|||
/// same page about which identifiers are used for what.
|
||||
///
|
||||
/// See [`FromEventsScope`] for recommendations on the usage.
|
||||
pub(crate) struct IntoEventsScope {
|
||||
/// Accesses the `AttrMap` from code in
|
||||
/// [`crate::field::FieldIteratorPart::Header`].
|
||||
pub(crate) attrs: Ident,
|
||||
pub(crate) struct AsItemsScope {
|
||||
/// Lifetime for data borrowed by the implementation.
|
||||
pub(crate) lifetime: Lifetime,
|
||||
}
|
||||
|
||||
impl IntoEventsScope {
|
||||
impl AsItemsScope {
|
||||
/// Create a fresh scope with all necessary identifiers.
|
||||
pub(crate) fn new() -> Self {
|
||||
pub(crate) fn new(lifetime: &Lifetime) -> Self {
|
||||
Self {
|
||||
attrs: Ident::new("attrs", Span::call_site()),
|
||||
lifetime: lifetime.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a reference to `ty`, borrowed for the lifetime of the AsXml
|
||||
/// impl.
|
||||
pub(crate) fn borrow(&self, ty: Type) -> Type {
|
||||
ref_ty(ty, self.lifetime.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn mangle_member(member: &Member) -> Ident {
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ impl State {
|
|||
/// the `advance` implementation of the state machine.
|
||||
///
|
||||
/// See [`FromEventsStateMachine::advance_match_arms`] and
|
||||
/// [`IntoEventsSubmachine::compile`] for the respective
|
||||
/// [`AsItemsSubmachine::compile`] for the respective
|
||||
/// requirements on the implementations.
|
||||
pub(crate) fn with_impl(mut self, body: TokenStream) -> Self {
|
||||
self.advance_body = body;
|
||||
|
|
@ -171,12 +171,12 @@ impl FromEventsSubmachine {
|
|||
}
|
||||
}
|
||||
|
||||
/// A partial [`IntoEventsStateMachine`] which only covers the builder for a
|
||||
/// A partial [`AsItemsStateMachine`] which only covers the builder for a
|
||||
/// single compound.
|
||||
///
|
||||
/// See [`IntoEventsStateMachine`] for more information on the state machines
|
||||
/// See [`AsItemsStateMachine`] for more information on the state machines
|
||||
/// in general.
|
||||
pub(crate) struct IntoEventsSubmachine {
|
||||
pub(crate) struct AsItemsSubmachine {
|
||||
/// Additional items necessary for the statemachine.
|
||||
pub(crate) defs: TokenStream,
|
||||
|
||||
|
|
@ -194,7 +194,7 @@ pub(crate) struct IntoEventsSubmachine {
|
|||
pub(crate) init: TokenStream,
|
||||
}
|
||||
|
||||
impl IntoEventsSubmachine {
|
||||
impl AsItemsSubmachine {
|
||||
/// Convert a partial state machine into a full state machine.
|
||||
///
|
||||
/// This converts the abstract [`State`] items into token
|
||||
|
|
@ -202,13 +202,13 @@ impl IntoEventsSubmachine {
|
|||
/// definitions and the match arms), rendering them effectively immutable.
|
||||
///
|
||||
/// This requires that the [`State::advance_body`] token streams evaluate
|
||||
/// to an `Option<rxml::Event>`. If it evaluates to `Some(.)`, that is
|
||||
/// to an `Option<Item>`. If it evaluates to `Some(.)`, that is
|
||||
/// emitted from the iterator. If it evaluates to `None`, the `advance`
|
||||
/// implementation is called again.
|
||||
///
|
||||
/// Each state implementation is augmented to also enter the next state,
|
||||
/// causing the iterator to terminate eventually.
|
||||
pub(crate) fn compile(self) -> IntoEventsStateMachine {
|
||||
pub(crate) fn compile(self) -> AsItemsStateMachine {
|
||||
let mut state_defs = TokenStream::default();
|
||||
let mut advance_match_arms = TokenStream::default();
|
||||
|
||||
|
|
@ -227,13 +227,13 @@ impl IntoEventsSubmachine {
|
|||
..
|
||||
}) => {
|
||||
quote! {
|
||||
::core::result::Result::Ok((::core::option::Option::Some(Self::#next_name { #construct_next }), event))
|
||||
::core::result::Result::Ok((::core::option::Option::Some(Self::#next_name { #construct_next }), item))
|
||||
}
|
||||
}
|
||||
// final state -> exit the state machine
|
||||
None => {
|
||||
quote! {
|
||||
::core::result::Result::Ok((::core::option::Option::None, event))
|
||||
::core::result::Result::Ok((::core::option::Option::None, item))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -244,17 +244,17 @@ impl IntoEventsSubmachine {
|
|||
|
||||
advance_match_arms.extend(quote! {
|
||||
Self::#name { #destructure } => {
|
||||
let event = #advance_body;
|
||||
let item = #advance_body;
|
||||
#footer
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
IntoEventsStateMachine {
|
||||
AsItemsStateMachine {
|
||||
defs: self.defs,
|
||||
state_defs,
|
||||
advance_match_arms,
|
||||
variants: vec![IntoEventsEntryPoint {
|
||||
variants: vec![AsItemsEntryPoint {
|
||||
init: self.init,
|
||||
destructure: self.destructure,
|
||||
}],
|
||||
|
|
@ -281,7 +281,7 @@ pub(crate) struct FromEventsEntryPoint {
|
|||
}
|
||||
|
||||
/// A single variant's entrypoint into the event iterator.
|
||||
pub(crate) struct IntoEventsEntryPoint {
|
||||
pub(crate) struct AsItemsEntryPoint {
|
||||
/// A pattern match which destructures the target type into its parts, for
|
||||
/// use by `init`.
|
||||
destructure: TokenStream,
|
||||
|
|
@ -468,7 +468,7 @@ impl FromEventsStateMachine {
|
|||
/// method. That method consumes the enum value and returns either a new enum
|
||||
/// value, an error, or the output type of the state machine.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct IntoEventsStateMachine {
|
||||
pub(crate) struct AsItemsStateMachine {
|
||||
/// Extra items which are needed for the state machine implementation.
|
||||
defs: TokenStream,
|
||||
|
||||
|
|
@ -480,7 +480,7 @@ pub(crate) struct IntoEventsStateMachine {
|
|||
/// enumeration type.
|
||||
///
|
||||
/// Each match arm must either diverge or evaluate to a
|
||||
/// `Result<(Option<State>, Option<Event>), xso::error::Error>`, where
|
||||
/// `Result<(Option<State>, Option<Item>), xso::error::Error>`, where
|
||||
/// where `State` is the state enumeration.
|
||||
///
|
||||
/// If `Some(.)` is returned for the event, that event is emitted. If
|
||||
|
|
@ -489,7 +489,7 @@ pub(crate) struct IntoEventsStateMachine {
|
|||
/// field.
|
||||
///
|
||||
/// If `None` is returned for the `Option<State>`, the iterator
|
||||
/// terminates yielding the `Option<Event>` value directly (even if it is
|
||||
/// terminates yielding the `Option<Item>` value directly (even if it is
|
||||
/// `None`). After the iterator has terminated, it yields `None`
|
||||
/// indefinitely.
|
||||
advance_match_arms: TokenStream,
|
||||
|
|
@ -498,10 +498,10 @@ pub(crate) struct IntoEventsStateMachine {
|
|||
///
|
||||
/// This may only contain more than one element if an enumeration is being
|
||||
/// serialised by the resulting state machine.
|
||||
variants: Vec<IntoEventsEntryPoint>,
|
||||
variants: Vec<AsItemsEntryPoint>,
|
||||
}
|
||||
|
||||
impl IntoEventsStateMachine {
|
||||
impl AsItemsStateMachine {
|
||||
/// Render the state machine as a token stream.
|
||||
///
|
||||
/// The token stream contains the following pieces:
|
||||
|
|
@ -515,7 +515,8 @@ impl IntoEventsStateMachine {
|
|||
vis: &Visibility,
|
||||
input_ty: &Type,
|
||||
state_ty_ident: &Ident,
|
||||
event_iter_ty_ident: &Ident,
|
||||
item_iter_ty_lifetime: &Lifetime,
|
||||
item_iter_ty: &Type,
|
||||
) -> Result<TokenStream> {
|
||||
let Self {
|
||||
defs,
|
||||
|
|
@ -525,10 +526,10 @@ impl IntoEventsStateMachine {
|
|||
} = self;
|
||||
|
||||
let input_ty_ref = make_ty_ref(input_ty);
|
||||
let docstr = format!("Convert a {0} into XML events.\n\nThis type is generated using the [`macro@xso::IntoXml`] derive macro and implements [`std::iter:Iterator`] for {0}.", input_ty_ref);
|
||||
let docstr = format!("Convert a {0} into XML events.\n\nThis type is generated using the [`macro@xso::AsXml`] derive macro and implements [`std::iter:Iterator`] for {0}.", input_ty_ref);
|
||||
|
||||
let init_body = if variants.len() == 1 {
|
||||
let IntoEventsEntryPoint { destructure, init } = variants.remove(0);
|
||||
let AsItemsEntryPoint { destructure, init } = variants.remove(0);
|
||||
quote! {
|
||||
{
|
||||
let #destructure = value;
|
||||
|
|
@ -537,7 +538,7 @@ impl IntoEventsStateMachine {
|
|||
}
|
||||
} else {
|
||||
let mut match_arms = TokenStream::default();
|
||||
for IntoEventsEntryPoint { destructure, init } in variants {
|
||||
for AsItemsEntryPoint { destructure, init } in variants {
|
||||
match_arms.extend(quote! {
|
||||
#destructure => #init,
|
||||
});
|
||||
|
|
@ -553,40 +554,40 @@ impl IntoEventsStateMachine {
|
|||
Ok(quote! {
|
||||
#defs
|
||||
|
||||
enum #state_ty_ident {
|
||||
enum #state_ty_ident<#item_iter_ty_lifetime> {
|
||||
#state_defs
|
||||
}
|
||||
|
||||
impl #state_ty_ident {
|
||||
fn advance(mut self) -> ::core::result::Result<(::core::option::Option<Self>, ::core::option::Option<::xso::exports::rxml::Event>), ::xso::error::Error> {
|
||||
impl<#item_iter_ty_lifetime> #state_ty_ident<#item_iter_ty_lifetime> {
|
||||
fn advance(mut self) -> ::core::result::Result<(::core::option::Option<Self>, ::core::option::Option<::xso::Item<#item_iter_ty_lifetime>>), ::xso::error::Error> {
|
||||
match self {
|
||||
#advance_match_arms
|
||||
}
|
||||
}
|
||||
|
||||
fn new(
|
||||
value: #input_ty,
|
||||
value: &#item_iter_ty_lifetime #input_ty,
|
||||
) -> ::core::result::Result<Self, ::xso::error::Error> {
|
||||
::core::result::Result::Ok(#init_body)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = #docstr]
|
||||
#vis struct #event_iter_ty_ident(::core::option::Option<#state_ty_ident>);
|
||||
#vis struct #item_iter_ty(::core::option::Option<#state_ty_ident<#item_iter_ty_lifetime>>);
|
||||
|
||||
impl ::std::iter::Iterator for #event_iter_ty_ident {
|
||||
type Item = ::core::result::Result<::xso::exports::rxml::Event, ::xso::error::Error>;
|
||||
impl<#item_iter_ty_lifetime> ::std::iter::Iterator for #item_iter_ty {
|
||||
type Item = ::core::result::Result<::xso::Item<#item_iter_ty_lifetime>, ::xso::error::Error>;
|
||||
|
||||
fn next(&mut self) -> ::core::option::Option<Self::Item> {
|
||||
let mut state = self.0.take()?;
|
||||
loop {
|
||||
let (next_state, ev) = match state.advance() {
|
||||
let (next_state, item) = match state.advance() {
|
||||
::core::result::Result::Ok(v) => v,
|
||||
::core::result::Result::Err(e) => return ::core::option::Option::Some(::core::result::Result::Err(e)),
|
||||
};
|
||||
if let ::core::option::Option::Some(ev) = ev {
|
||||
if let ::core::option::Option::Some(item) = item {
|
||||
self.0 = next_state;
|
||||
return ::core::option::Option::Some(::core::result::Result::Ok(ev));
|
||||
return ::core::option::Option::Some(::core::result::Result::Ok(item));
|
||||
}
|
||||
// no event, do we have a state?
|
||||
if let ::core::option::Option::Some(st) = next_state {
|
||||
|
|
@ -602,8 +603,8 @@ impl IntoEventsStateMachine {
|
|||
}
|
||||
}
|
||||
|
||||
impl #event_iter_ty_ident {
|
||||
fn new(value: #input_ty) -> ::core::result::Result<Self, ::xso::error::Error> {
|
||||
impl<#item_iter_ty_lifetime> #item_iter_ty {
|
||||
fn new(value: &#item_iter_ty_lifetime #input_ty) -> ::core::result::Result<Self, ::xso::error::Error> {
|
||||
#state_ty_ident::new(value).map(|ok| Self(::core::option::Option::Some(ok)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
//! Handling of structs
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::*;
|
||||
|
||||
|
|
@ -25,16 +25,19 @@ pub(crate) struct FromXmlParts {
|
|||
pub(crate) builder_ty_ident: Ident,
|
||||
}
|
||||
|
||||
/// Parts necessary to construct a `::xso::IntoXml` implementation.
|
||||
pub(crate) struct IntoXmlParts {
|
||||
/// Parts necessary to construct a `::xso::AsXml` implementation.
|
||||
pub(crate) struct AsXmlParts {
|
||||
/// Additional items necessary for the implementation.
|
||||
pub(crate) defs: TokenStream,
|
||||
|
||||
/// The body of the `::xso::IntoXml::into_event_iter` function.
|
||||
pub(crate) into_event_iter_body: TokenStream,
|
||||
/// The body of the `::xso::AsXml::as_xml_iter` function.
|
||||
pub(crate) as_xml_iter_body: TokenStream,
|
||||
|
||||
/// The name of the type which is the `::xso::IntoXml::EventIter`.
|
||||
pub(crate) event_iter_ty_ident: Ident,
|
||||
/// The type which is the `::xso::AsXml::ItemIter`.
|
||||
pub(crate) item_iter_ty: Type,
|
||||
|
||||
/// The lifetime name used in `item_iter_ty`.
|
||||
pub(crate) item_iter_ty_lifetime: Lifetime,
|
||||
}
|
||||
|
||||
/// Definition of a struct and how to parse it.
|
||||
|
|
@ -55,7 +58,7 @@ pub(crate) struct StructDef {
|
|||
builder_ty_ident: Ident,
|
||||
|
||||
/// Name of the iterator type.
|
||||
event_iter_ty_ident: Ident,
|
||||
item_iter_ty_ident: Ident,
|
||||
|
||||
/// Flag whether debug mode is enabled.
|
||||
debug: bool,
|
||||
|
|
@ -78,7 +81,7 @@ impl StructDef {
|
|||
inner: Compound::from_fields(fields)?,
|
||||
target_ty_ident: ident.clone(),
|
||||
builder_ty_ident: quote::format_ident!("{}FromXmlBuilder", ident),
|
||||
event_iter_ty_ident: quote::format_ident!("{}IntoXmlIterator", ident),
|
||||
item_iter_ty_ident: quote::format_ident!("{}AsXmlIterator", ident),
|
||||
debug: meta.debug.is_set(),
|
||||
})
|
||||
}
|
||||
|
|
@ -136,22 +139,53 @@ impl StructDef {
|
|||
})
|
||||
}
|
||||
|
||||
pub(crate) fn make_into_event_iter(&self, vis: &Visibility) -> Result<IntoXmlParts> {
|
||||
pub(crate) fn make_as_xml_iter(&self, vis: &Visibility) -> Result<AsXmlParts> {
|
||||
let xml_namespace = &self.namespace;
|
||||
let xml_name = &self.name;
|
||||
|
||||
let target_ty_ident = &self.target_ty_ident;
|
||||
let event_iter_ty_ident = &self.event_iter_ty_ident;
|
||||
let state_ty_ident = quote::format_ident!("{}State", event_iter_ty_ident);
|
||||
let item_iter_ty_ident = &self.item_iter_ty_ident;
|
||||
let item_iter_ty_lifetime = Lifetime {
|
||||
apostrophe: Span::call_site(),
|
||||
ident: Ident::new("xso_proc_as_xml_iter_lifetime", Span::call_site()),
|
||||
};
|
||||
let item_iter_ty = Type::Path(TypePath {
|
||||
qself: None,
|
||||
path: Path {
|
||||
leading_colon: None,
|
||||
segments: [PathSegment {
|
||||
ident: item_iter_ty_ident.clone(),
|
||||
arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
|
||||
colon2_token: None,
|
||||
lt_token: token::Lt {
|
||||
spans: [Span::call_site()],
|
||||
},
|
||||
args: [GenericArgument::Lifetime(item_iter_ty_lifetime.clone())]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
gt_token: token::Gt {
|
||||
spans: [Span::call_site()],
|
||||
},
|
||||
}),
|
||||
}]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
},
|
||||
});
|
||||
let state_ty_ident = quote::format_ident!("{}State", item_iter_ty_ident);
|
||||
|
||||
let defs = self
|
||||
.inner
|
||||
.make_into_event_iter_statemachine(&target_ty_ident.clone().into(), "Struct")?
|
||||
.make_as_item_iter_statemachine(
|
||||
&target_ty_ident.clone().into(),
|
||||
"Struct",
|
||||
&item_iter_ty_lifetime,
|
||||
)?
|
||||
.with_augmented_init(|init| {
|
||||
quote! {
|
||||
let name = (
|
||||
::xso::exports::rxml::Namespace::from(#xml_namespace),
|
||||
#xml_name.into(),
|
||||
::std::borrow::Cow::Borrowed(#xml_name),
|
||||
);
|
||||
#init
|
||||
}
|
||||
|
|
@ -165,15 +199,17 @@ impl StructDef {
|
|||
}
|
||||
.into(),
|
||||
&state_ty_ident,
|
||||
event_iter_ty_ident,
|
||||
&item_iter_ty_lifetime,
|
||||
&item_iter_ty,
|
||||
)?;
|
||||
|
||||
Ok(IntoXmlParts {
|
||||
Ok(AsXmlParts {
|
||||
defs,
|
||||
into_event_iter_body: quote! {
|
||||
#event_iter_ty_ident::new(self)
|
||||
as_xml_iter_body: quote! {
|
||||
#item_iter_ty_ident::new(self)
|
||||
},
|
||||
event_iter_ty_ident: event_iter_ty_ident.clone(),
|
||||
item_iter_ty,
|
||||
item_iter_ty_lifetime,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
use proc_macro2::Span;
|
||||
use syn::{spanned::Spanned, *};
|
||||
|
||||
/// Construct a [`syn::Type`] referring to `::xso::exports::rxml::QName`.
|
||||
pub(crate) fn qname_ty(span: Span) -> Type {
|
||||
/// Construct a [`syn::Type`] referring to `::xso::exports::rxml::Namespace`.
|
||||
pub(crate) fn namespace_ty(span: Span) -> Type {
|
||||
Type::Path(TypePath {
|
||||
qself: None,
|
||||
path: Path {
|
||||
|
|
@ -31,7 +31,7 @@ pub(crate) fn qname_ty(span: Span) -> Type {
|
|||
arguments: PathArguments::None,
|
||||
},
|
||||
PathSegment {
|
||||
ident: Ident::new("QName", span),
|
||||
ident: Ident::new("Namespace", span),
|
||||
arguments: PathArguments::None,
|
||||
},
|
||||
]
|
||||
|
|
@ -41,6 +41,83 @@ pub(crate) fn qname_ty(span: Span) -> Type {
|
|||
})
|
||||
}
|
||||
|
||||
/// Construct a [`syn::Type`] referring to `::xso::exports::rxml::NcNameStr`.
|
||||
pub(crate) fn ncnamestr_ty(span: Span) -> Type {
|
||||
Type::Path(TypePath {
|
||||
qself: None,
|
||||
path: Path {
|
||||
leading_colon: Some(syn::token::PathSep {
|
||||
spans: [span, span],
|
||||
}),
|
||||
segments: [
|
||||
PathSegment {
|
||||
ident: Ident::new("xso", span),
|
||||
arguments: PathArguments::None,
|
||||
},
|
||||
PathSegment {
|
||||
ident: Ident::new("exports", span),
|
||||
arguments: PathArguments::None,
|
||||
},
|
||||
PathSegment {
|
||||
ident: Ident::new("rxml", span),
|
||||
arguments: PathArguments::None,
|
||||
},
|
||||
PathSegment {
|
||||
ident: Ident::new("NcNameStr", span),
|
||||
arguments: PathArguments::None,
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct a [`syn::Type`] referring to `Cow<#lifetime, #ty>`.
|
||||
pub(crate) fn cow_ty(ty: Type, lifetime: Lifetime) -> Type {
|
||||
let span = ty.span();
|
||||
Type::Path(TypePath {
|
||||
qself: None,
|
||||
path: Path {
|
||||
leading_colon: Some(syn::token::PathSep {
|
||||
spans: [span, span],
|
||||
}),
|
||||
segments: [
|
||||
PathSegment {
|
||||
ident: Ident::new("std", span),
|
||||
arguments: PathArguments::None,
|
||||
},
|
||||
PathSegment {
|
||||
ident: Ident::new("borrow", span),
|
||||
arguments: PathArguments::None,
|
||||
},
|
||||
PathSegment {
|
||||
ident: Ident::new("Cow", span),
|
||||
arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
|
||||
colon2_token: None,
|
||||
lt_token: token::Lt { spans: [span] },
|
||||
args: [
|
||||
GenericArgument::Lifetime(lifetime),
|
||||
GenericArgument::Type(ty),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
gt_token: token::Gt { spans: [span] },
|
||||
}),
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct a [`syn::Type`] referring to
|
||||
/// `Cow<#lifetime, ::rxml::NcNameStr>`.
|
||||
pub(crate) fn ncnamestr_cow_ty(ty_span: Span, lifetime: Lifetime) -> Type {
|
||||
cow_ty(ncnamestr_ty(ty_span), lifetime)
|
||||
}
|
||||
|
||||
/// Construct a [`syn::Expr`] referring to
|
||||
/// `<#ty as ::xso::FromXmlText>::from_xml_text`.
|
||||
pub(crate) fn from_xml_text_fn(ty: Type) -> Expr {
|
||||
|
|
@ -79,8 +156,8 @@ pub(crate) fn from_xml_text_fn(ty: Type) -> Expr {
|
|||
}
|
||||
|
||||
/// Construct a [`syn::Expr`] referring to
|
||||
/// `<#ty as ::xso::IntoOptionalXmlText>::into_optional_xml_text`.
|
||||
pub(crate) fn into_optional_xml_text_fn(ty: Type) -> Expr {
|
||||
/// `<#ty as ::xso::AsOptionalXmlText>::as_optional_xml_text`.
|
||||
pub(crate) fn as_optional_xml_text_fn(ty: Type) -> Expr {
|
||||
let span = ty.span();
|
||||
Expr::Path(ExprPath {
|
||||
attrs: Vec::new(),
|
||||
|
|
@ -101,11 +178,11 @@ pub(crate) fn into_optional_xml_text_fn(ty: Type) -> Expr {
|
|||
arguments: PathArguments::None,
|
||||
},
|
||||
PathSegment {
|
||||
ident: Ident::new("IntoOptionalXmlText", span),
|
||||
ident: Ident::new("AsOptionalXmlText", span),
|
||||
arguments: PathArguments::None,
|
||||
},
|
||||
PathSegment {
|
||||
ident: Ident::new("into_optional_xml_text", span),
|
||||
ident: Ident::new("as_optional_xml_text", span),
|
||||
arguments: PathArguments::None,
|
||||
},
|
||||
]
|
||||
|
|
@ -185,8 +262,8 @@ pub(crate) fn string_ty(span: Span) -> Type {
|
|||
}
|
||||
|
||||
/// Construct a [`syn::Expr`] referring to
|
||||
/// `<#ty as ::xso::IntoXmlText>::into_xml_text`.
|
||||
pub(crate) fn into_xml_text_fn(ty: Type) -> Expr {
|
||||
/// `<#ty as ::xso::AsXmlText>::as_xml_text`.
|
||||
pub(crate) fn as_xml_text_fn(ty: Type) -> Expr {
|
||||
let span = ty.span();
|
||||
Expr::Path(ExprPath {
|
||||
attrs: Vec::new(),
|
||||
|
|
@ -207,11 +284,11 @@ pub(crate) fn into_xml_text_fn(ty: Type) -> Expr {
|
|||
arguments: PathArguments::None,
|
||||
},
|
||||
PathSegment {
|
||||
ident: Ident::new("IntoXmlText", span),
|
||||
ident: Ident::new("AsXmlText", span),
|
||||
arguments: PathArguments::None,
|
||||
},
|
||||
PathSegment {
|
||||
ident: Ident::new("into_xml_text", span),
|
||||
ident: Ident::new("as_xml_text", span),
|
||||
arguments: PathArguments::None,
|
||||
},
|
||||
]
|
||||
|
|
@ -293,3 +370,55 @@ pub(crate) fn text_codec_decode_fn(codec_ty: Type, for_ty: Type) -> Expr {
|
|||
path: ty.path,
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct a [`syn::Type`] for `&#lifetime #ty`.
|
||||
pub(crate) fn ref_ty(ty: Type, lifetime: Lifetime) -> Type {
|
||||
let span = ty.span();
|
||||
Type::Reference(TypeReference {
|
||||
and_token: token::And { spans: [span] },
|
||||
lifetime: Some(lifetime),
|
||||
mutability: None,
|
||||
elem: Box::new(ty),
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct a [`syn::Type`] referring to
|
||||
/// `::std::marker::PhantomData<&#lifetime ()>`.
|
||||
pub(crate) fn phantom_lifetime_ty(lifetime: Lifetime) -> Type {
|
||||
let span = lifetime.span();
|
||||
let dummy = Type::Tuple(TypeTuple {
|
||||
paren_token: token::Paren::default(),
|
||||
elems: punctuated::Punctuated::default(),
|
||||
});
|
||||
Type::Path(TypePath {
|
||||
qself: None,
|
||||
path: Path {
|
||||
leading_colon: Some(syn::token::PathSep {
|
||||
spans: [span, span],
|
||||
}),
|
||||
segments: [
|
||||
PathSegment {
|
||||
ident: Ident::new("std", span),
|
||||
arguments: PathArguments::None,
|
||||
},
|
||||
PathSegment {
|
||||
ident: Ident::new("marker", span),
|
||||
arguments: PathArguments::None,
|
||||
},
|
||||
PathSegment {
|
||||
ident: Ident::new("PhantomData", span),
|
||||
arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
|
||||
colon2_token: None,
|
||||
lt_token: token::Lt { spans: [span] },
|
||||
args: [GenericArgument::Type(ref_ty(dummy, lifetime))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
gt_token: token::Gt { spans: [span] },
|
||||
}),
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue