use std::convert::TryFrom;
use crate::{StateMachineInfo, TypestateError, generated_attr};
use parse::Parser;
use syn::{
parse, visit_mut::VisitMut, Attribute, Error, Field, Fields, Ident, Item, ItemMod, ItemStruct,
Path,
};
pub(crate) const AUTOMATA_ATTR_IDENT: &str = "automaton";
pub(crate) const STATE_ATTR_IDENT: &str = "state";
type Result<Ok, Err = Error> = ::core::result::Result<Ok, Err>;
pub(crate) fn visit_states(
module: &mut ItemMod,
state_machine_info: &mut StateMachineInfo,
constructor_ident: Option<Ident>,
) -> Vec<Error> {
let mut state_visitor = StateVisitor::new(state_machine_info, constructor_ident);
state_visitor.visit_item_mod_mut(module);
if !state_visitor.errors.is_empty() {
return state_visitor.errors;
}
let mut constructors = state_visitor.constructors;
if let Some((_, v)) = &mut module.content {
v.append(&mut constructors);
}
let sealed_trait = state_visitor.sealed_trait;
if sealed_trait.trait_ident.is_none() {
return vec![TypestateError::MissingAutomata.into()];
}
match &mut module.content {
Some((_, v)) => {
v.append(&mut sealed_trait.into()); }
None => {}
}
vec![]
}
struct StateVisitor<'sm> {
state_machine_info: &'sm mut StateMachineInfo,
sealed_trait: SealedPattern,
constructors: Vec<Item>,
constructor_ident: Option<Ident>,
errors: Vec<Error>,
}
impl<'sm> StateVisitor<'sm> {
fn new(
state_machine_info: &'sm mut StateMachineInfo,
constructor_ident: Option<Ident>,
) -> Self {
Self {
state_machine_info,
sealed_trait: SealedPattern::default(),
constructors: vec![],
constructor_ident,
errors: vec![],
}
}
fn push_multiple_attr_error(&mut self, attr: &Attribute) {
self.errors
.push(TypestateError::ConflictingAttributes(attr.clone()).into());
}
fn push_multiple_decl_error(&mut self, attr: &Attribute) {
self.errors
.push(TypestateError::DuplicateAttributes(attr.clone()).into());
}
fn push_multiple_automata_decl_error(&mut self, it: &ItemStruct) {
self.errors
.push(TypestateError::AutomataRedefinition(it.clone()).into());
}
}
#[derive(Default)]
pub(crate) struct SealedPattern {
trait_ident: Option<Ident>, state_idents: Vec<Ident>,
}
impl From<SealedPattern> for Vec<Item> {
fn from(sealed_pattern: SealedPattern) -> Self {
let trait_ident = sealed_pattern.trait_ident.expect("missing `.trait_ident`");
let private_mod_ident = ::quote::format_ident!("__private");
let private_mod_trait = &trait_ident;
let generated_attr = generated_attr();
let states = &sealed_pattern.state_idents;
let mut ret = vec![
::syn::parse_quote! {
#generated_attr
#[doc(hidden)]
mod #private_mod_ident {
#generated_attr
pub trait #private_mod_trait {}
}
},
::syn::parse_quote! {
#generated_attr
pub trait #trait_ident: #private_mod_ident::#private_mod_trait {}
},
::syn::parse_quote! {
#generated_attr
impl<__T : ?::core::marker::Sized> #trait_ident
for __T
where
__T : #private_mod_ident::#private_mod_trait,
{}
},
];
ret.extend(states.iter().map(|each_state| {
::syn::parse_quote! {
#generated_attr
impl #private_mod_ident::#private_mod_trait for #each_state {}
}
}));
ret
}
}
impl<'sm> VisitMut for StateVisitor<'sm> {
fn visit_item_struct_mut(&mut self, it_struct: &mut ItemStruct) {
let attributes = &mut it_struct.attrs;
let mut main_attr = None;
attributes.retain(|attr| {
Attr::Retain == {
let ts_attr = TypestateAttr::try_from(&attr.path);
match ts_attr {
Ok(inner_ts_attr) => {
match main_attr {
Some(ref prev_attr) => {
if *prev_attr == inner_ts_attr {
self.push_multiple_decl_error(attr);
} else {
self.push_multiple_attr_error(attr);
}
}
ref mut at_none @ None => {
*at_none = Some(inner_ts_attr)
}
}
Attr::Discard
}
Err(()) => Attr::Retain,
}
}
});
if !self.errors.is_empty() {
return;
}
match main_attr {
Some(TypestateAttr::Automata) => {
match self.state_machine_info.automaton_ident {
Some(_) => {
self.push_multiple_automata_decl_error(it_struct);
return;
}
None => self.state_machine_info.automaton_ident = Some(it_struct.clone()),
};
match it_struct.expand_state_type_parameter() {
Ok(bound_ident) => match self.sealed_trait.trait_ident {
Some(_) => unreachable!("this should have been checked previously"),
None => self.sealed_trait.trait_ident = Some(bound_ident),
},
Err(e) => {
self.errors.push(e);
}
}
}
Some(TypestateAttr::State) => {
self.state_machine_info.intermediate_automaton.add_state(it_struct.ident.clone());
self.state_machine_info.add_state(it_struct.clone().into());
self.sealed_trait.state_idents.push(it_struct.ident.clone());
if let Some(ident) = &self.constructor_ident {
self.constructors
.expand_state_constructors(ident, it_struct);
}
}
None => {
}
}
}
}
trait ExpandStateConstructors {
fn expand_state_constructors(&mut self, constructor_ident: &Ident, item_struct: &ItemStruct);
}
impl ExpandStateConstructors for Vec<Item> {
fn expand_state_constructors(&mut self, constructor_ident: &Ident, item_struct: &ItemStruct) {
if let Fields::Named(named) = &item_struct.fields {
let struct_ident = &item_struct.ident;
let field_ident = named.named.iter().map(|field| &field.ident);
let field_ident2 = named.named.iter().map(|field| &field.ident); let field_ty = named.named.iter().map(|field| &field.ty);
let generated_attr = generated_attr();
self.push(::syn::parse_quote! {
#generated_attr
impl #struct_ident {
pub fn #constructor_ident(#(#field_ident: #field_ty,)*) -> Self {
Self {
#(#field_ident2,)*
}
}
}
});
}
}
}
trait ExpandState {
fn expand_state_type_parameter(&mut self) -> syn::Result<Ident>;
}
impl ExpandState for ItemStruct {
fn expand_state_type_parameter(&mut self) -> syn::Result<Ident> {
let type_param_ident = ::quote::format_ident!("{}State", self.ident);
self.generics
.params
.push(::syn::parse_quote!(State: #type_param_ident));
let field_to_add = ::quote::quote!(
pub state: State
);
match &mut self.fields {
syn::Fields::Named(named) => {
named
.named
.push(Field::parse_named.parse2(field_to_add).unwrap());
}
syn::Fields::Unnamed(_) => {
return syn::Result::Err(TypestateError::UnsupportedStruct(self.clone()).into());
}
syn::Fields::Unit => {
self.fields = Fields::Named(::syn::parse_quote!({ #field_to_add }));
}
};
Ok(type_param_ident)
}
}
#[derive(Debug, PartialEq)]
enum TypestateAttr {
Automata,
State,
}
impl TryFrom<&Ident> for TypestateAttr {
type Error = ();
fn try_from(ident: &Ident) -> Result<Self, Self::Error> {
if ident == AUTOMATA_ATTR_IDENT {
Ok(Self::Automata)
} else if ident == STATE_ATTR_IDENT {
Ok(Self::State)
} else {
Err(())
}
}
}
impl TryFrom<&Path> for TypestateAttr {
type Error = ();
fn try_from(path: &Path) -> Result<Self, Self::Error> {
if path.is_ident(AUTOMATA_ATTR_IDENT) {
Ok(Self::Automata)
} else if path.is_ident(STATE_ATTR_IDENT) {
Ok(Self::State)
} else {
Err(())
}
}
}
#[derive(PartialEq)]
enum Attr {
Retain,
Discard,
}