use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{spanned::Spanned, *};
use crate::common::{FromEventsSubmachine, IntoEventsSubmachine, Scope, ScopeNamespace};
use crate::error_message::ParentRef;
use crate::field::{
FieldDef, FieldFromEventsPart, FieldIntoEventsPart, FieldTempInit, NestedMatcher,
};
use crate::types::*;
fn default_on_unknown_child(p: Option<Ident>) -> Expr {
match p {
Some(v) => unknown_child_policy_variant(v),
None => default_value(unknown_child_policy_ty(Span::call_site())),
}
}
fn default_on_unknown_attribute(p: Option<Ident>) -> Expr {
match p {
Some(v) => unknown_attribute_policy_variant(v),
None => default_value(unknown_attribute_policy_ty(Span::call_site())),
}
}
#[derive(Debug)]
pub(crate) struct Compound {
fields: Vec<FieldDef>,
namespace_field: Option<(Span, Type, Member)>,
on_unknown_child: Expr,
on_unknown_attribute: Expr,
}
impl Compound {
pub(crate) fn new<T: Iterator<Item = Result<FieldDef>>>(
on_unknown_child: Option<Ident>,
on_unknown_attribute: Option<Ident>,
input: T,
) -> Result<Self> {
let mut fields = Vec::with_capacity(input.size_hint().1.unwrap_or(0));
let mut text_field: Option<Span> = None;
let mut namespace_field: Option<(Span, Type, Member)> = None;
let mut collect_wildcard_field: Option<Span> = None;
for field in input {
let field = field?;
if let Some(ty) = field.namespace_field_type() {
if namespace_field.is_some() {
return Err(Error::new_spanned(
field.ident,
"only one #[xml(namespace)] field is allowed",
));
}
namespace_field = Some((field.span, ty.clone(), field.ident.clone()));
};
if field.kind.is_text() {
if text_field.is_some() {
return Err(Error::new_spanned(
field.ident,
"only one #[xml(text)] field is allowed",
));
}
text_field = Some(field.ident.span());
}
if field.kind.is_child_wildcard() {
if collect_wildcard_field.is_some() {
return Err(Error::new_spanned(
field.ident,
"only one #[xml(elements)] field without namespace/name selector is allowed",
));
}
collect_wildcard_field = Some(field.ident.span());
}
fields.push(field);
}
Ok(Self {
fields,
namespace_field,
on_unknown_child: default_on_unknown_child(on_unknown_child),
on_unknown_attribute: default_on_unknown_attribute(on_unknown_attribute),
})
}
pub(crate) fn from_fields(
on_unknown_child: Option<Ident>,
on_unknown_attribute: Option<Ident>,
fields: &Fields,
) -> Result<Self> {
Self::new(
on_unknown_child,
on_unknown_attribute,
fields.iter().enumerate().map(|(i, field)| {
FieldDef::from_field(field, i.try_into().expect("too many fields"))
}),
)
}
pub(crate) fn namespace_field(&self) -> Option<(Span, &Type, &Member)> {
self.namespace_field.as_ref().map(|(a, b, c)| (*a, b, c))
}
pub(crate) fn field_count(&self) -> usize {
self.fields.len()
}
pub(crate) fn build_from_events_builder(
&self,
scope_namespace: ScopeNamespace,
state_ty_ident: &Ident,
output_name: &ParentRef,
state_prefix: &str,
) -> Result<FromEventsSubmachine> {
let scope = Scope::new(
scope_namespace,
state_prefix,
state_ty_ident.clone(),
&Span::call_site(),
);
let Scope {
ref attrs,
ref text,
ref start_ev_qname,
ref start_ev_attrs,
ref substate_result,
ref default_state_ident,
..
} = scope;
let builder_data = Ident::new("__data", Span::call_site());
let substate_data = Ident::new("__substate_data", Span::call_site());
let readable_name = output_name.to_string();
let on_unknown_child = &self.on_unknown_child;
let on_unknown_attribute = &self.on_unknown_attribute;
let builder_data_ty_ident = quote::format_ident!("{}Data{}", state_ty_ident, state_prefix);
let builder_data_ty: Type = ty_from_ident(builder_data_ty_ident.clone()).into();
struct State {
name: Ident,
extra_decl: TokenStream,
extra_destructure: TokenStream,
advance_body: TokenStream,
uses_mut: Option<Ident>,
}
impl State {
fn new(name: Ident) -> Self {
Self {
name,
extra_decl: TokenStream::default(),
extra_destructure: TokenStream::default(),
advance_body: TokenStream::default(),
uses_mut: None,
}
}
fn add_field(&mut self, name: &Ident, ty: &Type) {
self.extra_decl.extend(quote! { #name: #ty, });
self.extra_destructure.extend(quote! { #name, });
}
fn with_field(mut self, name: &Ident, ty: &Type) -> Self {
self.add_field(name, ty);
self
}
fn with_impl(mut self, body: TokenStream) -> Self {
self.advance_body = body;
self
}
fn with_uses_mut(mut self, ident: &Ident) -> Self {
self.uses_mut = Some(ident.clone());
self
}
}
let mut states = Vec::new();
let mut struct_body = TokenStream::default();
let mut output_init = TokenStream::default();
let mut child_match = TokenStream::default();
let mut child_fallback: Option<TokenStream> = None;
let mut init_fields = TokenStream::default();
let mut collect_fields = TokenStream::default();
let mut process_text = TokenStream::default();
let mut inner_defs = TokenStream::default();
let is_tuple = !output_name.is_path();
for field in self.fields.iter() {
let field_ident = &field.ident;
let tempname = scope.mangle_field(field_ident);
let builder = field.build_from_events_builder(&scope, output_name, &tempname)?;
let read_expr = match builder {
FieldFromEventsPart::Nested {
temp:
FieldTempInit {
ty: temp_ty,
init: temp_init,
},
matcher,
builder,
collect,
finish,
extra_defs,
} => {
inner_defs.extend(extra_defs);
let substate = scope.mangle_state(field_ident);
struct_body.extend(quote! {
#tempname: #temp_ty,
});
init_fields.extend(quote! {
#tempname: #temp_init,
});
match matcher {
NestedMatcher::Inline(matcher) => {
child_match.extend(quote! {
let (#start_ev_qname, #start_ev_attrs) = match #matcher {
::std::result::Result::Ok(builder) => {
return ::std::result::Result::Ok(::std::ops::ControlFlow::Break(Self::#substate { #builder_data, #substate_data: builder }));
},
::std::result::Result::Err(::xso::FromEventsError::Mismatch{ name, attrs }) => (name, attrs),
::std::result::Result::Err(::xso::FromEventsError::Invalid(err)) => return ::std::result::Result::Err(err),
};
});
}
NestedMatcher::Fallback(fallback) => {
if child_fallback.is_some() {
return Err(Error::new(
Span::call_site(),
"multiple fields attempting to collect all children",
));
}
child_fallback = Some(quote! {
return ::std::result::Result::Ok(::std::ops::ControlFlow::Break(Self::#substate { #builder_data, #substate_data: #fallback }));
});
}
}
let feed = feed_fn(builder.clone());
states.push(State::new(substate.clone()).with_field(&substate_data, &builder).with_uses_mut(&substate_data).with_impl(quote! {
match #feed(&mut #substate_data, ev)? {
Some(#substate_result) => {
#collect
::std::result::Result::Ok(::std::ops::ControlFlow::Break(Self::#default_state_ident { #builder_data }))
}
None => {
::std::result::Result::Ok(::std::ops::ControlFlow::Break(Self::#substate { #builder_data, #substate_data }))
}
}
}));
quote! {
#finish
}
}
FieldFromEventsPart::Init {
value:
FieldTempInit {
ty: temp_ty,
init: temp_init,
},
} => {
collect_fields.extend(quote! {
let #tempname: #temp_ty = #temp_init;
});
init_fields.extend(quote! {
#tempname,
});
struct_body.extend(quote! {
#tempname: #temp_ty,
});
quote! {
#builder_data.#tempname
}
}
FieldFromEventsPart::Text {
temp:
FieldTempInit {
ty: temp_ty,
init: temp_init,
},
accum,
finalize,
} => {
init_fields.extend(quote! {
#tempname: #temp_init,
});
struct_body.extend(quote! {
#tempname: #temp_ty,
});
process_text.extend(accum);
quote! {
#finalize
}
}
};
if is_tuple {
output_init.extend(quote! {
#read_expr,
});
} else {
output_init.extend(quote! {
#field_ident: #read_expr,
});
}
}
let child_fallback = if let Some(child_fallback) = child_fallback {
child_fallback
} else {
let discard_state_ident = quote::format_ident!("{}Discard", state_prefix);
let ty = discard_events_ty(Span::call_site());
let feed = feed_fn(ty.clone());
states.push(State::new(discard_state_ident.clone()).with_field(&substate_data, &ty).with_uses_mut(&substate_data).with_impl(quote! {
match #feed(&mut #substate_data, ev)? {
Some(()) => {
::std::result::Result::Ok(::std::ops::ControlFlow::Break(Self::#default_state_ident { #builder_data }))
}
None => {
::std::result::Result::Ok(::std::ops::ControlFlow::Break(Self::#discard_state_ident { #builder_data, #substate_data }))
}
}
}));
quote! {
#on_unknown_child.trigger(concat!("Unknown child in ", #readable_name, "."))?;
return ::std::result::Result::Ok(::std::ops::ControlFlow::Break(Self::#discard_state_ident { #builder_data, #substate_data: #ty::new() }));
}
};
let output_cons = match output_name {
ParentRef::Named(ref path) => {
quote! {
#path { #output_init }
}
}
ParentRef::Unnamed { .. } | ParentRef::Wrapper { .. } => {
quote! {
( #output_init )
}
}
};
let attr_enforcement = quote! {
if let Some(_) = #attrs.into_iter().next() {
#on_unknown_attribute.trigger(concat!("Unknown attribute in ", #readable_name, "."))?;
}
};
let init = quote! {
{
#collect_fields
#attr_enforcement
::std::result::Result::Ok(#state_ty_ident::#default_state_ident {
#builder_data: #builder_data_ty_ident {
#init_fields
},
})
}
};
states.push(State::new(default_state_ident.clone()).with_impl(quote! {
match ev {
::xso::exports::rxml::Event::EndElement(_) => {
::std::result::Result::Ok(::std::ops::ControlFlow::Continue(#output_cons))
}
::xso::exports::rxml::Event::Text(_, #text) => {
#process_text
::std::result::Result::Ok(::std::ops::ControlFlow::Break(Self::#default_state_ident { #builder_data })) }
::xso::exports::rxml::Event::StartElement(_, #start_ev_qname, #start_ev_attrs) => {
#child_match
#child_fallback
}
::xso::exports::rxml::Event::XmlDeclaration(_, _) => {
::std::result::Result::Ok(::std::ops::ControlFlow::Break(Self::#default_state_ident { #builder_data }))
},
}
}));
let mut advance_match_arms = TokenStream::default();
let mut state_enum_body = TokenStream::default();
for state in states {
let State {
name,
extra_decl,
extra_destructure,
uses_mut,
advance_body,
} = state;
state_enum_body.extend(quote! {
#name { #builder_data: #builder_data_ty, #extra_decl },
});
let extra_init = if let Some(ident) = uses_mut {
quote! { let mut #ident = #ident; }
} else {
TokenStream::default()
};
advance_match_arms.extend(quote! {
Self::#name { mut #builder_data, #extra_destructure } => {
#extra_init
#advance_body
},
});
}
Ok(FromEventsSubmachine {
defs: quote! {
#inner_defs
struct #builder_data_ty_ident {
#struct_body
}
},
state_defs: state_enum_body,
advance_match_arms,
init,
})
}
pub(crate) fn build_into_events_iterator(
&self,
scope_namespace: ScopeNamespace,
input_name: &ParentRef,
state_prefix: &str,
state_ty_ident: &Ident,
attrs_ident: Option<&Ident>,
) -> Result<IntoEventsSubmachine> {
let scope = Scope::new(
scope_namespace,
state_prefix,
state_ty_ident.clone(),
&Span::call_site(),
);
let Scope { ref attrs, .. } = scope;
let start_element_ident = quote::format_ident!("{}StartElement", state_prefix);
let end_element_ident = quote::format_ident!("{}EndElement", state_prefix);
struct Variant {
name: Ident,
decl: TokenStream,
destructure: TokenStream,
body: TokenStream,
uses_mut: Option<Ident>,
}
impl Variant {
fn new(name: Ident) -> Self {
Self {
name,
decl: TokenStream::default(),
destructure: TokenStream::default(),
body: TokenStream::default(),
uses_mut: None,
}
}
fn add_field(&mut self, name: &Ident, ty: &Type) {
self.decl.extend(quote! { #name: #ty, });
self.destructure.extend(quote! { #name, });
}
fn with_field(mut self, name: &Ident, ty: &Type) -> Self {
self.add_field(name, ty);
self
}
fn with_impl(mut self, body: TokenStream) -> Self {
self.body = body;
self
}
fn with_uses_mut(mut self, ident: &Ident) -> Self {
self.uses_mut = Some(ident.clone());
self
}
}
let mut state_enum_variants: Vec<Variant> = Vec::new();
let mut inner_defs = TokenStream::default();
let mut start_element_init = quote! {
qname: qname,
};
let mut destructure_struct = TokenStream::default();
let mut destructure_tuple = TokenStream::default();
let mut passive_fields: Vec<(Ident, Member, Type)> = Vec::new();
state_enum_variants.push(Variant::new(start_element_ident.clone()).with_field(
&Ident::new("qname", Span::call_site()),
&qname_ty(Span::call_site()),
));
let mut start_element_body = if let Some(ident) = attrs_ident.as_ref() {
start_element_init.extend(quote! {
attrs: #ident,
});
state_enum_variants[0].add_field(
&Ident::new("attrs", Span::call_site()),
&attr_map_ty(Span::call_site()),
);
quote! {
let mut #attrs = attrs;
{ let _ = &mut #attrs; };
}
} else {
quote! {
let mut #attrs = ::xso::exports::rxml::AttrMap::new();
{ let _ = &mut #attrs; };
}
};
for field in self.fields.iter() {
let field_ident = &field.ident;
let tempname = scope.mangle_field(field_ident);
destructure_struct.extend(quote! {
#field_ident: #tempname,
});
destructure_tuple.extend(quote! {
#tempname,
});
match field.build_into_events_iterator(&scope, input_name, &tempname)? {
FieldIntoEventsPart::Header { setter } => {
state_enum_variants[0].add_field(&tempname, &field.ty);
start_element_init.extend(quote! {
#tempname: #tempname,
});
start_element_body.extend(quote! {
#setter
});
}
FieldIntoEventsPart::ContentConsume { prepare, emitter } => {
start_element_init.extend(quote! {
#tempname: #prepare,
});
for variant in state_enum_variants.iter_mut() {
variant.add_field(&tempname, &field.ty);
}
state_enum_variants.push(
Variant::new(scope.mangle_state(field_ident))
.with_field(&tempname, &field.ty)
.with_impl(quote! {
#emitter
}),
);
}
FieldIntoEventsPart::ContentMut {
extra_defs,
init,
ty,
emitter,
} => {
inner_defs.extend(extra_defs);
start_element_init.extend(quote! {
#tempname: #init,
});
for variant in state_enum_variants.iter_mut() {
variant.add_field(&tempname, &ty);
}
state_enum_variants.push(
Variant::new(scope.mangle_state(field_ident))
.with_field(&tempname, &ty)
.with_uses_mut(&tempname)
.with_impl(quote! {
#emitter
}),
);
}
FieldIntoEventsPart::Passive => {
passive_fields.push((tempname, field.ident.clone(), field.ty.clone()));
}
}
}
state_enum_variants[0].body = quote! {
{
#start_element_body
::std::option::Option::Some(::xso::exports::rxml::Event::StartElement(
::xso::exports::rxml::parser::EventMetrics::zero(),
qname,
#attrs,
))
}
};
state_enum_variants.push(Variant::new(end_element_ident.clone()).with_impl(quote! {
::std::option::Option::Some(::xso::exports::rxml::Event::EndElement(::xso::exports::rxml::parser::EventMetrics::zero()))
}));
let mut end_element_body_prefix = TokenStream::default();
for (name, _, ty) in passive_fields {
for variant in state_enum_variants.iter_mut() {
variant.add_field(&name, &ty);
}
start_element_init.extend(quote! {
#name: #name,
});
end_element_body_prefix.extend(quote! {
let _ = #name;
});
}
{
let end_element = state_enum_variants.last_mut().unwrap();
end_element_body_prefix.extend(end_element.body.clone());
end_element.body = quote! {
{
#end_element_body_prefix
}
};
}
let mut advance_impl = TokenStream::default();
let mut state_enum_body = TokenStream::default();
for (
i,
Variant {
ref name,
ref decl,
ref destructure,
ref body,
ref uses_mut,
},
) in state_enum_variants.iter().enumerate()
{
let footer = match state_enum_variants.get(i + 1) {
Some(Variant {
name: ref next_name,
destructure: ref construct_next,
..
}) => {
quote! {
let next_state = Self::#next_name { #construct_next };
::std::result::Result::Ok((::std::option::Option::Some(next_state), event))
}
}
None => {
quote! {
::std::result::Result::Ok((::std::option::Option::None, event))
}
}
};
if let Some(uses_mut) = uses_mut.as_ref() {
advance_impl.extend(quote! {
Self::#name { #destructure } => {
let mut #uses_mut = #uses_mut;
match #body {
::std::option::Option::Some(event) => {
::std::result::Result::Ok((::std::option::Option::Some(Self::#name { #destructure }), ::std::option::Option::Some(event)))
},
event => { #footer },
}
},
});
} else {
advance_impl.extend(quote! {
Self::#name { #destructure } => {
let event = #body;
#footer
},
});
}
state_enum_body.extend(quote! {
#name { #decl },
});
}
Ok(IntoEventsSubmachine {
defs: inner_defs,
state_defs: state_enum_body,
advance_match_arms: advance_impl,
destructure: match input_name {
ParentRef::Named(name) => quote! { #name { #destructure_struct } },
ParentRef::Unnamed { .. } | ParentRef::Wrapper { .. } => {
quote! { ( #destructure_tuple ) }
}
},
init: quote! {
Self::#start_element_ident { #start_element_init }
},
})
}
pub(crate) fn single_type(&self) -> Option<&Type> {
if self.fields.len() != 1 {
None
} else {
Some(&self.fields[0].ty)
}
}
pub(crate) fn as_dyn(&self) -> Option<DynCompound<'_>> {
let (_, ty, member) = self.namespace_field.as_ref()?;
Some(DynCompound {
namespace_ty: ty,
namespace_member: member,
fields: &self.fields,
})
}
pub(crate) fn as_tuple_ty(&self) -> Type {
let mut ty = TypeTuple {
paren_token: syn::token::Paren::default(),
elems: syn::punctuated::Punctuated::new(),
};
for field in self.fields.iter() {
ty.elems.push(field.ty.clone());
}
Type::Tuple(ty)
}
}
pub(crate) struct DynCompound<'x> {
namespace_ty: &'x Type,
namespace_member: &'x Member,
fields: &'x [FieldDef],
}
impl<'x> DynCompound<'x> {
pub(crate) fn namespace_ty(&self) -> &'x Type {
self.namespace_ty
}
pub(crate) fn build_get_namespace(
&self,
mut access_field: impl FnMut(Member) -> Expr,
) -> Result<TokenStream> {
let member = access_field(self.namespace_member.clone());
Ok(quote! {
&#member
})
}
pub(crate) fn build_set_namespace(
&self,
input: &Ident,
mut access_field: impl FnMut(Member) -> Expr,
) -> Result<TokenStream> {
let member = access_field(self.namespace_member.clone());
let mut field_impls = quote! {};
for field in self.fields {
let field_impl = field.build_set_namespace(input, &mut access_field);
field_impls.extend(field_impl);
}
Ok(quote! {
#field_impls
#member = #input;
})
}
}