use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote, ToTokens};
use syn::Field;
use crate::{field::FieldAugment, state::State};
pub fn generate_builder_struct(
name: &Ident,
fields: syn::punctuated::Iter<'_, Field>,
) -> TokenStream {
let mut field_declarations = Vec::new();
for field in fields {
let field_ident = &field.ident;
let ty = &field.ty;
field_declarations.push(quote! {
#field_ident: &'a mut #ty
});
}
quote! {
pub struct #name<'a, State> {
_phantom: std::marker::PhantomData<State>,
#(#field_declarations),*
}
}
}
pub fn generate_impl_for_all_states(
locker_struct_name: &Ident,
states: &[State],
all_fields: &syn::Fields,
) -> TokenStream {
let mut impl_states = TokenStream::new();
for state in states {
let state_ident = state.ident();
let mut functions = Vec::new();
for complement in &state.complements {
let complement_state = state.add_state(complement);
let complement_fn = format_ident!(
"{}",
complement.ident.as_ref().expect("Fields must be named")
);
let complement_ident = complement_state.ident();
let fields_iter = all_fields.iter().map(|f| &f.ident);
let fields_iter2 = all_fields.iter().map(|f| &f.ident);
functions.push(quote! {
pub fn #complement_fn(self) -> #locker_struct_name<'a, #complement_ident<'a>> {
#locker_struct_name{
_phantom: std::marker::PhantomData,
#(#fields_iter: self.#fields_iter2),*
}
}
});
}
let idents: Vec<&Ident> = state
.active
.iter()
.map(|a| a.ident.as_ref().expect("All fields must be named"))
.collect();
let locked_fields = state.locked_fields();
let parameters = if !state.active.is_empty() {
quote! {<'a>}
} else {
quote! {}
};
let asyncrocity = if state.is_async() {
quote! {async}
} else {
quote! {}
};
let (result_left, result_right, return_statement) =
if state.active.iter().any(|f| f.is_result()) {
(
quote! {
Result<
},
quote! {
, Box<dyn std::error::Error + 'a>>
},
quote! {
Ok(
#state_ident{#(#idents),*}
)
},
)
} else {
(quote! {}, quote! {}, quote! {#state_ident{#(#idents),*}})
};
let lock_method = if !state.active.is_empty() {
quote! {
pub #asyncrocity fn lock(self) -> #result_left #state_ident #parameters #result_right {
#(#locked_fields)*
#return_statement
}
}
} else {
quote! {}
};
quote! {
impl<'a> #locker_struct_name<'a, #state_ident #parameters> {
#(#functions)*
#lock_method
}
}
.to_tokens(&mut impl_states)
}
impl_states
}
pub fn generate_trait_implementation(
struct_identifier: &Ident,
locker_struct_name: &Ident,
empty_identifier: &Ident,
all_fields: &syn::Fields,
) -> TokenStream {
let struct_fields = all_fields
.iter()
.filter_map(|f| Some(format_ident!("{}", f.ident.as_ref()?)));
quote! {
impl<'a> Locker<'a> for #struct_identifier {
type LockBuilder=#locker_struct_name<'a, #empty_identifier>;
fn locker(&'a mut self) -> Self::LockBuilder {
Self::LockBuilder{_phantom: std::marker::PhantomData,#(#struct_fields: &mut self.#struct_fields),*}
}
}
}
}
pub fn generate_state_struct_declarations(states: &[State]) -> TokenStream {
let mut definitions = TokenStream::new();
for state in states {
let state_name = state.ident();
let state_fields_ident = state.active.iter().map(|f| {
f.ident
.as_ref()
.expect("All fields must be named")
.to_owned()
});
let state_fields_return_type = state.active.iter().map(Field::return_type);
let deref_trait = if true {
quote! {std::ops::DerefMut}
} else {
quote! {std::ops::Deref}
};
let parameters = if !state.active.is_empty() {
quote! {<'a>}
} else {
quote! {}
};
quote! {
pub struct #state_name #parameters {
#(pub #state_fields_ident: Box<dyn #deref_trait<Target = #state_fields_return_type> + 'a>),*
}
}.to_tokens(&mut definitions)
}
definitions
}