use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::*;
use crate::common::{make_ty_ref, FromEventsParts, IntoEventIterParts, ItemDef, Scope};
use crate::compound::Compound;
use crate::error_message::ParentRef;
use crate::field::{
Field, FieldDef, FieldFromEventsPart, FieldIntoEventsPart, FieldTempInit, NestedMatcher,
};
use crate::meta::{NamespaceRef, NodeFilterMeta};
use crate::structs::{StructInner, StructNamespace};
use crate::types::option_ty;
use crate::types::*;
#[derive(Debug)]
struct WrappedField {
ty_ident: Ident,
inner: Box<dyn ItemDef>,
}
impl Field for WrappedField {
fn build_from_events_builder(
&self,
scope: &Scope,
container_name: &ParentRef,
_tempname: Ident,
member: &Member,
_ty: &Type,
) -> Result<FieldFromEventsPart> {
let Scope {
ref start_ev_qname,
ref start_ev_attrs,
ref substate_result,
..
} = scope;
let access = scope.access_field(member);
let inner_ty = ty_from_ident(self.ty_ident.clone()).into();
let inner_name = ParentRef::from(Path::from(self.ty_ident.clone()));
let readable_name = container_name.to_string();
let missingerr = quote! {
concat!("Required child missing in ", #readable_name, ".")
};
let duperr = quote! {
concat!("Only one child allowed in ", #readable_name, ".")
};
let FromEventsParts {
struct_def,
from_events_body,
builder_ty_ident,
} = self.inner.build_from_events_builder(
&Visibility::Inherited,
&inner_ty,
&inner_name,
start_ev_qname,
start_ev_attrs,
)?;
let inner_ty_builder_ty = ty_from_ident(builder_ty_ident).into();
Ok(FieldFromEventsPart::Nested {
extra_defs: quote! {
#struct_def
},
temp: FieldTempInit {
ty: option_ty(inner_ty),
init: quote! { ::std::option::Option::None },
},
matcher: NestedMatcher::Inline(quote! {
#from_events_body.and_then(|ok| {
if #access.is_some() {
::std::result::Result::Err(::xso::FromEventsError::Invalid(::xso::error::Error::ParseError(#duperr)))
} else {
::std::result::Result::Ok(ok)
}
})
}),
builder: inner_ty_builder_ty,
collect: quote! {
#access = ::std::option::Option::Some(#substate_result);
},
finish: quote! {
if let ::std::option::Option::Some(v) = #access {
v
} else {
return ::std::result::Result::Err(::xso::error::Error::ParseError(#missingerr));
}
},
})
}
fn build_into_events_iterator(
&self,
_scope: &Scope,
_container_name: &ParentRef,
tempname: Ident,
_member: &Member,
_ty: &Type,
) -> Result<FieldIntoEventsPart> {
let inner_ty = ty_from_ident(self.ty_ident.clone()).into();
let inner_name = ParentRef::from(Path::from(self.ty_ident.clone()));
let IntoEventIterParts {
struct_def,
into_event_iter_body,
event_iter_ty_ident,
} = self.inner.build_into_events_iterator(
&Visibility::Inherited,
&inner_ty,
&inner_name,
&tempname,
)?;
let inner_ty_iter_ty = ty_from_ident(event_iter_ty_ident).into();
Ok(FieldIntoEventsPart::ContentMut {
extra_defs: struct_def,
init: quote! {
{
let mut #tempname = #tempname;
#into_event_iter_body?
}
},
ty: inner_ty_iter_ty,
emitter: quote! {
match #tempname.next() {
::std::option::Option::Some(::std::result::Result::Ok(ev)) => ::std::option::Option::Some(ev),
::std::option::Option::Some(::std::result::Result::Err(e)) => return ::std::result::Result::Err(e),
::std::option::Option::None => ::std::option::Option::None,
}
},
})
}
fn build_set_namespace(
&self,
_input: &Ident,
_ty: &Type,
_access: Expr,
) -> Result<TokenStream> {
unreachable!()
}
}
#[derive(Debug)]
pub(crate) struct Wrapped {
inner: StructInner,
builder: Ident,
iterator: Ident,
debug: bool,
}
impl ItemDef for Wrapped {
fn build_from_events_builder(
&self,
vis: &Visibility,
output_ty: &Type,
output_name: &ParentRef,
name_ident: &Ident,
attrs_ident: &Ident,
) -> Result<FromEventsParts> {
let builder_ty_ident = &self.builder;
let state_ty_ident = quote::format_ident!("{}State", builder_ty_ident);
let inner_ident = quote::format_ident!("{}Inner", builder_ty_ident);
let inner_ty = Type::Tuple(TypeTuple {
paren_token: syn::token::Paren::default(),
elems: [output_ty.clone()].into_iter().collect(),
});
let defs = self
.inner
.build_from_events_builder(&state_ty_ident, &output_name.wrapper(), "Identity")?
.render(
&Visibility::Inherited,
&inner_ident,
&state_ty_ident,
&inner_ty,
None,
)?;
let feed = feed_fn(ty_from_ident(inner_ident.clone()).into());
let output_ty_ref = make_ty_ref(output_ty);
let docstr = format!("Build a {0} from XML events.\n\nThis type is generated using the [`macro@xso::FromXml`] derive macro and implements [`xso::FromEventsBuilder`] for {0}.", output_ty_ref);
Ok(FromEventsParts {
struct_def: quote! {
#defs
#[doc = #docstr]
#vis struct #builder_ty_ident (#inner_ident);
impl ::xso::FromEventsBuilder for #builder_ty_ident {
type Output = #output_ty;
fn feed(&mut self, ev: ::xso::exports::rxml::Event) -> ::std::result::Result<::std::option::Option<Self::Output>, ::xso::error::Error> {
#feed(&mut self.0, ev).map(|ok| ok.map(|some| some.0))
}
}
impl #builder_ty_ident {
fn new(name: ::xso::exports::rxml::QName, attrs: ::xso::exports::rxml::AttrMap) -> ::std::result::Result<Self, ::xso::FromEventsError> {
#inner_ident::new(name, attrs).map(Self)
}
}
},
from_events_body: quote! {
#builder_ty_ident::new(#name_ident, #attrs_ident)
},
builder_ty_ident: builder_ty_ident.clone(),
})
}
fn build_into_events_iterator(
&self,
vis: &Visibility,
input_ty: &Type,
input_name: &ParentRef,
self_ident: &Ident,
) -> Result<IntoEventIterParts> {
let event_iter_ty_ident = &self.iterator;
let state_ty_ident = quote::format_ident!("{}State", event_iter_ty_ident);
let inner_ty = Type::Tuple(TypeTuple {
paren_token: syn::token::Paren::default(),
elems: [input_ty.clone()].into_iter().collect(),
});
let statemachine = self
.inner
.build_into_events_iterator(&input_name.wrapper(), &state_ty_ident, "Identity")?
.render(vis, &inner_ty, &state_ty_ident, event_iter_ty_ident)?;
Ok(IntoEventIterParts {
struct_def: statemachine,
into_event_iter_body: quote! {
#event_iter_ty_ident::new((#self_ident,))
},
event_iter_ty_ident: event_iter_ty_ident.clone(),
})
}
fn build_dyn_namespace(&self) -> Result<TokenStream> {
return Err(Error::new(
Span::call_site(),
"namespace = dyn cannot be combined with wrapped(..)",
));
}
fn debug_mode(&self) -> bool {
self.debug
}
}
pub(crate) fn wrap(
span: &Span,
meta: NodeFilterMeta,
ty_ident: &Ident,
builder_name: Ident,
iterator_name: Ident,
inner: Box<dyn ItemDef>,
) -> Result<Box<Wrapped>> {
let debug = inner.debug_mode();
let namespace = match meta.namespace {
Some(NamespaceRef::Static(ns)) => ns,
Some(NamespaceRef::Dyn(ns)) => {
return Err(Error::new_spanned(
ns,
"dyn namespace is not supported for wrappers",
))
}
Some(NamespaceRef::Super(ns)) => {
return Err(Error::new_spanned(
ns,
"super namespace is not supported for wrappers",
))
}
None => {
return Err(Error::new(
*span,
"namespace is required for wrappers (inside `#[xml(.., wrapped_with(..))]`)",
))
}
};
let name = match meta.name {
Some(name) => name.into(),
None => {
return Err(Error::new(
*span,
"name is required for wrappers (inside `#[xml(.., wrapped_with(..))]`)",
))
}
};
let field = WrappedField {
ty_ident: ty_ident.clone(),
inner,
};
let inner = Compound::new(
None,
None,
[Ok(FieldDef {
span: *span,
ident: Member::Unnamed(Index {
index: 0,
span: *span,
}),
ty: Type::Never(TypeNever {
bang_token: token::Not { spans: [*span] },
}),
kind: Box::new(field),
})]
.into_iter(),
)?;
let inner = StructInner::Compound {
namespace: StructNamespace::Static(namespace),
name,
inner,
};
Ok(Box::new(Wrapped {
inner,
builder: builder_name,
iterator: iterator_name,
debug,
}))
}