use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::*;
use crate::common::Scope;
use crate::error_message::{self, ParentRef};
use crate::meta::{Name, NameRef, NamespaceRef, StaticNamespace};
use crate::types::*;
use super::{Field, FieldFromEventsPart, FieldIntoEventsPart, FieldTempInit, NestedMatcher};
#[derive(Debug)]
pub(crate) struct FlagField {
namespace: StaticNamespace,
name: Name,
}
impl FlagField {
pub(super) fn new(
attr_span: &Span,
namespace: Option<NamespaceRef>,
name: Option<NameRef>,
) -> Result<Self> {
let namespace = match namespace {
None => {
return Err(Error::new(
attr_span.clone(),
"#[xml(flag)] requires namespace attribute",
))
}
Some(NamespaceRef::Static(ns)) => ns,
Some(NamespaceRef::Dyn(ns)) => {
return Err(Error::new_spanned(
ns,
"dynamic namespaces cannot be used with #[xml(flag)]",
))
}
Some(NamespaceRef::Super(ns)) => {
return Err(Error::new_spanned(
ns,
"flag elements cannot refer to the parent namespace",
))
}
};
let name = match name {
None => {
return Err(Error::new(
attr_span.clone(),
"#[xml(flag)] requires name attribute",
))
}
Some(name) => name,
};
Ok(Self {
namespace,
name: name.into(),
})
}
}
impl Field for FlagField {
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 child_name = &self.name;
let child_namespace = &self.namespace;
let access = scope.access_field(member);
let duperr = error_message::on_duplicate_child(container_name, member);
Ok(FieldFromEventsPart::Nested {
extra_defs: TokenStream::default(),
temp: FieldTempInit {
ty: Type::Path(TypePath {
qself: None,
path: Ident::new("bool", Span::call_site()).into(),
}),
init: quote! { false },
},
matcher: NestedMatcher::Inline(quote! {
if #start_ev_qname.0 == #child_namespace && #start_ev_qname.1 == #child_name {
if #access {
::std::result::Result::Err(::xso::FromEventsError::Invalid(::xso::error::Error::ParseError(#duperr)))
} else {
::std::result::Result::Ok(::xso::DiscardEvents::new())
}
} else {
::std::result::Result::Err(::xso::FromEventsError::Mismatch { name: #start_ev_qname, attrs: #start_ev_attrs })
}
}),
builder: discard_events_ty(Span::call_site()),
collect: quote! {
let _ = #substate_result;
#access = true;
},
finish: quote! {
#access
},
})
}
fn build_into_events_iterator(
&self,
_scope: &Scope,
_container_name: &ParentRef,
tempname: Ident,
_member: &Member,
_ty: &Type,
) -> Result<FieldIntoEventsPart> {
let child_name = &self.name;
let child_namespace = &self.namespace;
let state_ty = primitive_ty("u8", Span::call_site());
Ok(FieldIntoEventsPart::ContentMut {
extra_defs: TokenStream::default(),
ty: state_ty,
init: quote! {
if #tempname {
0
} else {
255
}
},
emitter: quote! {
match #tempname {
0 => {
#tempname = 1;
::std::option::Option::Some(::xso::exports::rxml::Event::StartElement(
::xso::exports::rxml::parser::EventMetrics::zero(),
(
::xso::exports::rxml::Namespace::try_from(#child_namespace)?,
::xso::exports::rxml::NcName::try_from(#child_name)?,
),
::xso::exports::rxml::AttrMap::new(),
))
},
1 => {
#tempname = 2;
::std::option::Option::Some(::xso::exports::rxml::Event::EndElement(
::xso::exports::rxml::parser::EventMetrics::zero(),
))
},
_ => ::std::option::Option::None,
}
},
})
}
fn build_set_namespace(
&self,
_input: &Ident,
_ty: &Type,
_access: Expr,
) -> Result<TokenStream> {
Ok(TokenStream::default())
}
}