use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::*;
use crate::state::{FromEventsSubmachine, IntoEventsSubmachine, State};
use crate::types::qname_ty;
pub(crate) struct Compound;
impl Compound {
pub(crate) fn from_fields(compound_fields: &Fields) -> Result<Self> {
match compound_fields {
Fields::Unit => (),
other => {
return Err(Error::new_spanned(
other,
"cannot derive on non-unit struct (yet!)",
))
}
}
Ok(Self)
}
pub(crate) fn make_from_events_statemachine(
&self,
state_ty_ident: &Ident,
output_cons: &Path,
state_prefix: &str,
) -> Result<FromEventsSubmachine> {
let default_state_ident = quote::format_ident!("{}Default", state_prefix);
let builder_data_ident = quote::format_ident!("__data");
let builder_data_ty: Type = TypePath {
qself: None,
path: quote::format_ident!("{}Data{}", state_ty_ident, state_prefix).into(),
}
.into();
let mut states = Vec::new();
let readable_name = output_cons.to_token_stream().to_string();
let unknown_attr_err = format!("Unknown attribute in {} element.", readable_name);
let unknown_child_err = format!("Unknown child in {} element.", readable_name);
states.push(State::new_with_builder(
default_state_ident.clone(),
&builder_data_ident,
&builder_data_ty,
).with_impl(quote! {
match ev {
::xso::exports::rxml::Event::EndElement(_) => {
::core::result::Result::Ok(::std::ops::ControlFlow::Continue(
#output_cons
))
}
::xso::exports::rxml::Event::StartElement(..) => {
::core::result::Result::Err(::xso::error::Error::Other(#unknown_child_err))
}
::xso::exports::rxml::Event::Text(..) => {
::core::result::Result::Err(::xso::error::Error::Other("Unexpected text content".into()))
}
::xso::exports::rxml::Event::XmlDeclaration(_, ::xso::exports::rxml::XmlVersion::V1_0) => ::core::result::Result::Ok(::std::ops::ControlFlow::Break(
Self::#default_state_ident { #builder_data_ident }
))
}
}));
Ok(FromEventsSubmachine {
defs: quote! {
struct #builder_data_ty;
},
states,
init: quote! {
if attrs.len() > 0 {
return ::core::result::Result::Err(::xso::error::Error::Other(
#unknown_attr_err,
).into());
}
::core::result::Result::Ok(#state_ty_ident::#default_state_ident {
#builder_data_ident: #builder_data_ty,
})
},
})
}
pub(crate) fn make_into_event_iter_statemachine(
&self,
input_name: &Path,
state_prefix: &str,
) -> Result<IntoEventsSubmachine> {
let start_element_state_ident = quote::format_ident!("{}StartElement", state_prefix);
let end_element_state_ident = quote::format_ident!("{}EndElement", state_prefix);
let name_ident = quote::format_ident!("name");
let mut states = Vec::new();
states.push(
State::new(start_element_state_ident.clone())
.with_field(&name_ident, &qname_ty(Span::call_site()))
.with_impl(quote! {
::core::option::Option::Some(::xso::exports::rxml::Event::StartElement(
::xso::exports::rxml::parser::EventMetrics::zero(),
#name_ident,
::xso::exports::rxml::AttrMap::new(),
))
}),
);
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(),
))
}),
);
Ok(IntoEventsSubmachine {
defs: TokenStream::default(),
states,
destructure: quote! {
#input_name
},
init: quote! {
Self::#start_element_state_ident { #name_ident }
},
})
}
}