use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::*;
use crate::common::Scope;
use crate::error_message::{self, ParentRef};
use crate::meta::{FlagOr, Name, NameRef, NamespaceRef, StaticNamespace};
use crate::structs::ElementSelector;
use crate::types::*;
use super::{Field, FieldFromEventsPart, FieldIntoEventsPart, FieldTempInit, NestedMatcher};
#[derive(Debug)]
pub(crate) struct ElementField {
selector: ElementSelector,
default_: FlagOr<Path>,
}
impl ElementField {
pub(super) fn new(
_attr_span: &Span,
namespace: Option<NamespaceRef>,
name: Option<NameRef>,
default_: FlagOr<Path>,
) -> Result<Self> {
let namespace = match namespace {
None => None,
Some(NamespaceRef::Static(ns)) => Some(ns),
Some(NamespaceRef::Dyn(ns)) => {
return Err(Error::new_spanned(
ns,
"dynamic namespaces cannot be used with #[xml(element)]",
))
}
Some(NamespaceRef::Super(ns)) => {
return Err(Error::new_spanned(
ns,
"collected elements cannot refer to the parent namespace",
))
}
};
let name = name.map(Name::from);
let selector = match (namespace, name) {
(Some(namespace), Some(name)) => ElementSelector::Qualified { namespace, name },
(Some(namespace), None) => ElementSelector::ByNamespace(namespace),
(None, Some(name)) => ElementSelector::ByName(name),
(None, None) => ElementSelector::Any,
};
Ok(Self { selector, default_ })
}
}
impl Field for ElementField {
fn build_from_events_builder(
&self,
scope: &Scope,
container_name: &ParentRef,
_tempname: Ident,
member: &Member,
ty: &Type,
) -> Result<FieldFromEventsPart> {
let Scope {
ref start_ev_attrs,
ref start_ev_qname,
ref substate_result,
..
} = scope;
let access = scope.access_field(member);
let test = self.selector.build_test(start_ev_qname);
let missingerr = error_message::on_missing_child(container_name, member);
let duperr = error_message::on_duplicate_child(container_name, member);
let on_missing = match self.default_ {
FlagOr::Absent => {
quote! {
return Err(::xso::error::Error::ParseError(#missingerr));
}
}
FlagOr::Present(_) => default_value(ty.clone()).into_token_stream(),
FlagOr::Value { ref value, .. } => {
quote! {
#value()
}
}
};
let dupcheck = match self.selector {
ElementSelector::Any => quote! {},
_ => quote! {
.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)
}
})
},
};
let matcher = NestedMatcher::Inline(quote! {
if #test {
<::xso::exports::minidom::Element as ::xso::FromXml>::from_events(#start_ev_qname, #start_ev_attrs) #dupcheck
} else {
::std::result::Result::Err(::xso::FromEventsError::Mismatch { name: #start_ev_qname, attrs: #start_ev_attrs})
}
});
Ok(FieldFromEventsPart::Nested {
extra_defs: TokenStream::default(),
temp: FieldTempInit {
ty: option_ty(element_ty(Span::call_site())),
init: quote! { ::std::option::Option::None },
},
matcher,
builder: from_xml_builder_ty(element_ty(Span::call_site())),
collect: quote! {
#access = ::std::option::Option::Some(#substate_result);
},
finish: quote! {
if let ::std::option::Option::Some(value) = #access {
value.into()
} else {
#on_missing
}
},
})
}
fn build_into_events_iterator(
&self,
_scope: &Scope,
_container_name: &ParentRef,
tempname: Ident,
_member: &Member,
_ty: &Type,
) -> Result<FieldIntoEventsPart> {
Ok(FieldIntoEventsPart::ContentMut {
extra_defs: TokenStream::default(),
ty: option_ty(event_iter_ty(element_ty(Span::call_site()))),
init: quote! {
::std::option::Option::<::xso::exports::minidom::Element>::from(#tempname).map(<::xso::exports::minidom::Element as ::xso::IntoXml>::into_event_iter).transpose()?
},
emitter: quote! {
match #tempname.as_mut().and_then(|x| x.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> {
Ok(TokenStream::default())
}
}
#[derive(Debug)]
pub(crate) struct ElementsField {
pub(super) selector: Option<(StaticNamespace, Option<Name>)>,
}
impl ElementsField {
pub(super) fn new(
_attr_span: &Span,
namespace: Option<NamespaceRef>,
name: Option<NameRef>,
) -> Result<Self> {
let namespace = match namespace {
None => {
if let Some(name) = name {
return Err(Error::new_spanned(
name,
"#[xml(elements(..))] cannot be used with an unnamespaced name",
));
}
None
}
Some(NamespaceRef::Static(ns)) => Some(ns),
Some(NamespaceRef::Dyn(ns)) => {
return Err(Error::new_spanned(
ns,
"dynamic namespaces cannot be used with #[xml(elements)]",
))
}
Some(NamespaceRef::Super(ns)) => {
return Err(Error::new_spanned(
ns,
"collected elements cannot refer to the parent namespace",
))
}
};
Ok(Self {
selector: namespace.map(|x| (x, name.map(|x| x.into()))),
})
}
}
impl Field for ElementsField {
fn is_child_wildcard(&self) -> bool {
match self.selector {
None => true,
_ => false,
}
}
fn build_from_events_builder(
&self,
scope: &Scope,
_container_name: &ParentRef,
_tempname: Ident,
member: &Member,
ty: &Type,
) -> Result<FieldFromEventsPart> {
let Scope {
ref start_ev_attrs,
ref start_ev_qname,
ref substate_result,
..
} = scope;
let access = scope.access_field(member);
let builder_cons = quote! {
<::xso::exports::minidom::Element as ::xso::FromXml>::from_events(#start_ev_qname, #start_ev_attrs)
};
let matcher = match self.selector {
Some((ref field_namespace, ref field_name)) => {
let mut condition = quote! {
#start_ev_qname.0 == #field_namespace
};
if let Some(field_name) = field_name {
condition.extend(quote! { && #start_ev_qname.1 == #field_name });
}
NestedMatcher::Inline(quote! {
if #condition {
#builder_cons
} else {
::std::result::Result::Err(::xso::FromEventsError::Mismatch { name: #start_ev_qname, attrs: #start_ev_attrs })
}
})
}
None => NestedMatcher::Fallback(quote! {
match #builder_cons {
::std::result::Result::Ok(v) => v,
::std::result::Result::Err(::xso::FromEventsError::Invalid(e)) => return ::std::result::Result::Err(e),
::std::result::Result::Err(::xso::FromEventsError::Mismatch { .. }) => unreachable!(),
}
}),
};
Ok(FieldFromEventsPart::Nested {
extra_defs: TokenStream::default(),
temp: FieldTempInit {
ty: ty.clone(),
init: quote! { ::std::vec::Vec::new() },
},
matcher,
builder: from_xml_builder_ty(element_ty(Span::call_site())),
collect: quote! {
#access.push(#substate_result);
},
finish: quote! {
#access
},
})
}
fn build_into_events_iterator(
&self,
_scope: &Scope,
_container_name: &ParentRef,
tempname: Ident,
_member: &Member,
_ty: &Type,
) -> Result<FieldIntoEventsPart> {
let state_ty = Type::Tuple(TypeTuple {
paren_token: syn::token::Paren::default(),
elems: [
vec_into_iter_ty(element_ty(Span::call_site())),
option_ty(event_iter_ty(element_ty(Span::call_site()))),
]
.into_iter()
.collect(),
});
Ok(FieldIntoEventsPart::ContentMut {
extra_defs: TokenStream::default(),
ty: state_ty,
init: quote! {
(#tempname.into_iter(), ::std::option::Option::None)
},
emitter: quote! {
loop {
if let ::std::option::Option::Some(current) = #tempname.1.as_mut() {
if let ::std::option::Option::Some(item) = current.next() {
break ::std::option::Option::Some(item?);
}
}
if let ::std::option::Option::Some(item) = #tempname.0.next() {
#tempname.1 = ::std::option::Option::Some(<::xso::exports::minidom::Element as ::xso::IntoXml>::into_event_iter(item)?);
} else {
break ::std::option::Option::None;
}
}
},
})
}
fn build_set_namespace(
&self,
_input: &Ident,
_ty: &Type,
_access: Expr,
) -> Result<TokenStream> {
Ok(TokenStream::default())
}
}