use syn::{parse::Parse,parse_quote,TypePath,FnArg, ImplItemFn, Path,PatIdent,TypeReference, Type, Receiver };
use crate::{error, RWLOCK, MUTEX, ARC, model::{Lib,ConstVars,ModelMethod1}};
use quote::{format_ident, quote, ToTokens};
use proc_macro2::TokenStream;
use proc_macro_error::abort;
use std::fmt::Display;
#[derive(Clone,Eq,PartialEq)]
pub enum ModelReceiver {
ArcRwLock,
ArcMutex,
Slf,
}
impl ModelReceiver{
pub fn type_path_rcvr_variants<T>(&self, ty: &T,lib: &Lib ) -> Vec<T>
where T: ToTokens + Parse
{
match self {
Self::ArcRwLock => { Self::get_arc_wraped(RWLOCK,ty, lib)},
Self::ArcMutex => { Self::get_arc_wraped(MUTEX,ty, lib) },
Self::Slf => { vec![ parse_quote!(#ty) ] },
}
}
pub fn get_arc_wraped<T,D>(lock:D, ty_path: &T, lib: &Lib) -> Vec<T>
where T: ToTokens + Parse,
D: Display,
{
let arcs = Self::get_paths(ARC, &Lib::Std);
let locks = Self::get_paths(&lock, lib);
arcs.iter().flat_map(|a|
locks.iter().map(move |l|
parse_quote!(#a<#l<#ty_path>>)
)
)
.collect::<Vec<T>>()
}
pub fn get_paths<D:Display>(ty_name: D , lib: &Lib) -> Vec<Path>{
let ident_lib = &format_ident!("{lib}");
let ident_type = &format_ident!("{ty_name}");
let mut paths = vec![
parse_quote!{ #ident_lib::sync::#ident_type },
parse_quote!{ sync::#ident_type },
parse_quote!{ #ident_type }
];
if lib.is_std() {
let std_spec = std::iter::once(parse_quote!{ ::std::sync::#ident_type });
paths.extend(std_spec);
}
paths
}
pub fn get_model_type<T>(&self,ty: &T, lib: &Lib) -> T
where T: ToTokens + Parse
{
let crate_ident = &format_ident!("{lib}");
let lead_colon = if lib.is_std() { quote!{ :: } } else { quote!{} };
match self{
Self::ArcRwLock => parse_quote!( ::std::sync::Arc< #lead_colon #crate_ident ::sync::RwLock<#ty>> ),
Self::ArcMutex => parse_quote!( ::std::sync::Arc< #lead_colon #crate_ident ::sync::Mutex<#ty>> ),
Self::Slf => parse_quote!(#ty),
}
}
pub fn get_model_wrap<T>(&self,ty: &T, lib: &Lib ) -> TokenStream
where T: ToTokens
{
let crate_ident = &format_ident!("{lib}");
let lead_colon = if lib.is_std() { quote!{ :: } } else { quote!{} };
match self{
Self::ArcRwLock => quote!{ ::std::sync::Arc::new( #lead_colon #crate_ident::sync::RwLock::new(#ty)) },
Self::ArcMutex => quote!{ ::std::sync::Arc::new( #lead_colon #crate_ident::sync::Mutex::new(#ty)) },
Self::Slf => quote!{#ty},
}
}
pub fn is_rcvr(&self, check_ty: &Type, act_ty: &Type, lib: &Lib ) -> bool {
let big_slf: Type = parse_quote!{ Self };
let rcvr_type_vrnts = self.type_path_rcvr_variants(act_ty,lib);
let rcvr_self_vrnts = self.type_path_rcvr_variants( &big_slf,lib);
rcvr_type_vrnts.iter().any(|p| p.eq(check_ty)) ||
rcvr_self_vrnts.iter().any(|p| p.eq(check_ty))
}
pub fn second_sort(&self, mut met: ImplItemFn , actor_type: &TypePath, lib: &Lib ) -> ModelMethod1 {
if let Some(input_rcvr) = met.sig.inputs.first_mut() {
if let FnArg::Typed(pat_ty) = input_rcvr.clone() {
if let syn::Pat::Ident( PatIdent{ ident,mutability,.. } ) = &*pat_ty.pat {
if ident == crate::ACTOR {
let actor_type = &Type::Path(actor_type.clone());
if let Type::Reference( TypeReference { elem, mutability,.. } ) = &*pat_ty.ty {
if let Some(mut_token) = mutability {
if !self.is_slf() {
abort!(mut_token,error::NOT_ALLOW_FAMILY_DIRECT_MUT_REF);
}
}
if self.is_rcvr(&*elem, actor_type, lib){
*input_rcvr = parse_quote!( & #mutability self );
return ModelMethod1::Ref( met, mutability.is_some() );
}
}
if self.is_rcvr(&*pat_ty.ty,actor_type, lib){
*input_rcvr = parse_quote!( #mutability self );
return ModelMethod1::Slf(met);
}
}
}
}
}
return ModelMethod1::Stat(met);
}
pub fn remove_mut(&self, met: &mut ImplItemFn ){
if let Some( input_rcvr ) = met.sig.inputs.first_mut(){
if let FnArg::Receiver(Receiver{mutability,..}) = input_rcvr {
*mutability = None;
}
}
}
pub fn is_slf(&self) -> bool {
if let Self::Slf = self {
return true;
}
false
}
pub fn get_lock(&self, lib: &Lib, const_vars: &ConstVars, is_mut: bool , is_stat: bool ) -> TokenStream {
if is_stat { return quote!{};}
let ConstVars{ actor,..} = &const_vars;
let unwrap_await = if lib.is_std() { quote!{.unwrap()} } else { quote!{ .await } };
match self {
Self::ArcMutex => {
let mut_token = if is_mut { quote!{mut} } else { quote!{} };
quote!{ let #mut_token #actor = actor.lock() #unwrap_await; }
},
Self::ArcRwLock => {
let (mut_token, read_write) =
if is_mut {
( quote!{mut}, quote!{ write } )
} else {
( quote!{}, quote!{ read } )
};
quote!{ let #mut_token #actor = #actor . #read_write () #unwrap_await; }
},
Self::Slf => { quote!{} },
}
}
}
impl Default for ModelReceiver{
fn default() -> Self {
Self::Slf
}
}