use std::collections::{HashMap, HashSet};
use syn::{
parse_quote,Generics,WherePredicate,GenericParam,GenericArgument,
Type,ItemImpl,TypePath,Signature,TypeParamBound,
Lifetime, LifetimeParam, PredicateLifetime, PredicateType, TypeParam
};
use proc_macro2::TokenStream;
use proc_macro_error::abort_call_site;
use super::ModelPhantomData;
use quote::quote;
pub struct GenWork {
pub impl_gen: Generics,
org_arg_param_map: HashMap<GenericArgument,GenericParam>,
pub arg_param_map: HashMap<GenericArgument,GenericParam>,
is_send: bool,
}
impl GenWork {
pub fn new(impl_gen: &Generics,is_send: bool) -> Self {
let arg_param_map: HashMap<GenericArgument,GenericParam> =
impl_gen.params
.iter()
.cloned()
.filter_map(|item|
if gen_params::is_const(&item){
None
} else {
Some((gen_params::as_arg(&item), item))
}
)
.collect();
let org_arg_param_map = arg_param_map.clone();
Self { impl_gen: impl_gen.clone(), org_arg_param_map, arg_param_map, is_send }
}
pub fn retain_io(&mut self, sig : &Signature ){
self.arg_param_map.retain(|k,_| !crate::model::includes(&sig, k ));
}
pub fn retain_i(&mut self, sig : &Signature ){
self.arg_param_map.retain(|k,_| !crate::model::includes(&sig.inputs, k ));
}
pub fn take_bounds(&mut self, sig : &mut Signature ){
if let Some(w_c ) = &sig.generics.where_clause.take(){
self.impl_gen.make_where_clause().predicates.extend(
w_c.predicates.iter().cloned()
);
}
}
pub fn difference(&self) -> Vec<GenericParam> {
let mut loc = Vec::new();
for (arg,param) in self.org_arg_param_map.iter() {
if !self.arg_param_map.contains_key(arg){
loc.push(param.clone());
}
}
loc
}
pub fn get_mod_gen(&mut self, full: bool) -> ModelGenerics {
let param_set = self.arg_param_map.keys().collect::<HashSet<_>>();
if full || param_set.is_empty(){
add_model_bounds(&mut self.impl_gen);
return ModelGenerics::new(self.impl_gen.clone());
}
let mut script_gen: Generics = Default::default();
let mut private_gen: Generics = Default::default();
let mut live_gen = self.impl_gen.clone();
for p in self.impl_gen.params.iter() {
if param_set.contains(&gen_params::as_arg(p)) {
private_gen.params.push(p.clone());
} else {
script_gen.params.push(p.clone());
}
}
for wp in self.impl_gen.make_where_clause().predicates.iter() {
if param_set.iter().any(|p| crate::model::includes(wp,p)){
private_gen.make_where_clause().predicates.push(wp.clone());
} else {
script_gen.make_where_clause().predicates.push(wp.clone());
}
}
let phantom_data = ModelPhantomData::from( &self.arg_param_map);
if self.is_send {
add_model_bounds(&mut live_gen);
} else {
for param in script_gen.params.iter(){
if !gen_params::is_const(param){
add_bounds(param, &mut live_gen);
}
}
}
add_model_bounds(&mut script_gen);
ModelGenerics{
script_gen,
private_gen,
live_gen,
phantom_data,
}
}
}
#[derive(Clone)]
pub struct ModelGenerics {
pub script_gen: Generics,
pub live_gen: Generics,
pub private_gen: Generics,
pub phantom_data: ModelPhantomData,
}
impl ModelGenerics {
pub fn new(gen:Generics) -> Self {
Self {
script_gen: gen.clone(),
live_gen: gen,
private_gen: Generics::default(),
phantom_data: ModelPhantomData::default(),
}
}
pub fn get_script_live_impl_block(&self, script_type: &TypePath, live_type: &TypePath) -> (ItemImpl,ItemImpl) {
let mut script: ItemImpl = parse_quote!( impl #script_type {} );
let mut live: ItemImpl = parse_quote!( impl #live_type {} );
script.generics = self.script_gen.clone();
live.generics = self.live_gen.clone();
(script,live)
}
}
pub fn add_model_bounds(gen: &mut Generics){
let params = gen.params.clone();
for param in params.iter() {
if !gen_params::is_const(param){
add_bounds(param, gen);
}
}
}
pub fn add_bounds( param: &GenericParam, gen: &mut Generics ){
if let GenericParam::Lifetime(LifetimeParam{lifetime: param_lifetime,bounds: param_bounds,..}) = param {
let static_lifetime: &Lifetime = &parse_quote!{ 'static };
if param_bounds.iter().any(|pblt| *pblt == *static_lifetime ){ return; }
for pred in gen.make_where_clause().predicates.iter_mut() {
if let WherePredicate::Lifetime(PredicateLifetime{ lifetime,bounds,..}) = pred {
if param_lifetime == lifetime {
if bounds.iter().any(|pblt| *pblt == *static_lifetime ){ return; }
bounds.push(static_lifetime.clone());
return;
}
}
}
gen.make_where_clause().predicates.push( parse_quote!( #param_lifetime : #static_lifetime));
return;
}
if let GenericParam::Type(TypeParam{ident,bounds: param_bounds,.. }) = param{
let param_ty: Type = parse_quote!{ #ident };
let mut model_bounds: Vec<TypeParamBound> =
vec![
parse_quote!{ Send },
parse_quote!{ Sync },
parse_quote!{ 'static },
];
if let Some(pos) = model_bounds.iter().position(|mb| param_bounds.iter().any(|pb| *mb == *pb )){
model_bounds.remove(pos);
}
if model_bounds.is_empty(){ return; }
for pred in gen.make_where_clause().predicates.iter_mut() {
if let WherePredicate::Type(PredicateType{bounded_ty,bounds,..}) = pred {
if param_ty.eq(bounded_ty){
for mb in model_bounds.clone(){
if !bounds.iter().any(|b| mb.eq(b)){
bounds.push(mb);
}
}
return;
}
}
}
gen.make_where_clause().predicates.push( parse_quote!( #param_ty : #(#model_bounds)+* ));
return;
}
}
pub fn get_some_turbo( gen: &Generics ) -> Option<TokenStream>{
if !gen.params.is_empty(){
let (_,gen_ty,_) = gen.split_for_impl();
let turbo_gen = gen_ty.as_turbofish();
Some(quote!{ #turbo_gen })
} else { None }
}
pub mod turbofish {
use proc_macro2::Span;
use super::{TypePath,abort_call_site};
use syn::{ Path,Type, PathArguments};
pub fn from_type_path(type_path: &TypePath) -> TypePath {
let mut segments = type_path.path.segments.clone();
if let Some(mut pair) = segments.pop() {
if let PathArguments::AngleBracketed(ref mut angle_bracketed) = pair.value_mut().arguments {
angle_bracketed.colon2_token = Some(syn::Token, Span::call_site()]));
segments.push(pair.value().clone());
let modified_path = Path {
leading_colon: type_path.path.leading_colon,
segments,
};
return TypePath {
qself: type_path.qself.clone(),
path: modified_path,
};
} else {
return type_path.clone();
}
}
abort_call_site!("Internal Error. 'generic::actor_turbofish::from_type_path'.'syn::Type::Path' is empty ");
}
pub fn from_type(ty: &Type) -> TypePath {
if let Type::Path(type_path) = ty {
return from_type_path(type_path);
} else {
abort_call_site!("Internal Error. 'generic::actor_turbofish::from_type'.expected 'syn::Type::Path' variant ");
}
}
}
pub mod gen_params {
use syn::{GenericParam,GenericArgument,Ident,parse_quote};
pub fn get_ident(param: &GenericParam) -> Ident {
match param {
GenericParam::Const(ct) => ct.ident.clone(),
GenericParam::Lifetime(lt) => lt.lifetime.ident.clone(),
GenericParam::Type(ty) => ty.ident.clone(),
}
}
pub fn as_arg(param: &GenericParam ) -> GenericArgument {
match param {
GenericParam::Lifetime(lt) => {
let lt = <.lifetime;
parse_quote!{ #lt }
},
_ => {
let ident = get_ident(¶m);
parse_quote!{ #ident }
},
}
}
pub fn is_const(param: &GenericParam) -> bool {
if let GenericParam::Const(_) = param {
return true;
}
false
}
}