use std::borrow::BorrowMut;
use std::collections::BTreeMap;
use std::ops::Deref;
use std::ops::DerefMut;
use proc_macro2::Ident;
use proc_macro2::Span;
use syn::parse_quote;
use syn::parse_str;
use syn::punctuated::Punctuated;
use syn::token;
use syn::visit_mut::visit_angle_bracketed_generic_arguments_mut;
use syn::visit_mut::visit_type_mut;
use syn::visit_mut::VisitMut;
use syn::Arm;
use syn::Binding;
use syn::GenericArgument;
use syn::ItemTrait;
use syn::Token;
use syn::TraitBound as SynTraitBound;
use syn::TraitItem;
use syn::TraitItemMethod;
use syn::TraitItemType;
use syn::Type;
use syn::TypeParam;
use crate::factory::TraitBound;
use crate::utils::UniqueHashId;
use super::ret::return_default_ret_type;
use super::ret::return_panic;
use super::T_SHM;
use super::sig::VariantSig;
use super::standard::StandardTrait;
use super::standard::TraitSchematic;
#[derive(Clone, Hash, Debug)]
pub struct Blueprint<'bound> {
pub ty: Option<Box<Type>>,
pub bound: &'bound TraitBound,
pub schematic: TraitSchematic,
pub methods: BTreeMap<Ident, Vec<Arm>>,
}
#[repr(transparent)]
#[derive(Default, Hash, Debug)]
pub struct BlueprintsMap<'bound>(BTreeMap<UniqueHashId<Type>, Vec<Blueprint<'bound>>>);
struct MonomorphizeFnSignature<'poly>(&'poly BTreeMap<Ident, &'poly Type>);
#[allow(dead_code)]
struct MonomorphizeTraitBound<'poly>(&'poly BTreeMap<Ident, &'poly Type>);
struct RemoveBoundBindings;
impl<'bound> Blueprint<'bound> {
pub fn get_associated_methods(&self) -> Vec<TraitItemMethod> {
let mut method_items = vec![];
let polymap = self.get_bound_generics().map(|types| {
self.get_schematic_generics()
.zip(types)
.map(|(gen, ty)| (gen.ident.clone(), ty))
.collect::<BTreeMap<_, _>>()
});
for method in self.get_schematic_methods() {
if let Some(method_arms) = self.methods.get(&method.sig.ident) {
let TraitItemMethod { ref sig, .. } = method;
let mut signature = sig.clone();
if let Some(polymap) = polymap.as_ref() {
MonomorphizeFnSignature(polymap).visit_signature_mut(&mut signature)
}
let default_return = match &signature.output {
syn::ReturnType::Default => quote::quote!(()),
syn::ReturnType::Type(_, ty) => {
return_default_ret_type(ty).unwrap_or_else(return_panic)
}
};
let item: TraitItemMethod = parse_quote!(
#signature { match self { #(#method_arms,)* _ => #default_return } }
);
method_items.push(item);
}
}
method_items
}
pub fn get_mapped_bindings(&self) -> Option<Vec<TraitItemType>> {
let mut types = self.get_schematic_types().collect::<Vec<_>>();
let Some(bindings) = self.get_bound_bindings() else {
for matc in types.iter_mut() {
if matc.default.is_none() && self.ty.is_some() {
let ty = &self.ty;
let bound = &self.bound;
let ident = &matc.ident;
let generics = &matc.generics;
matc.default = Some((
token::Eq(Span::call_site()),
parse_quote!(
<#ty as #bound>::#ident #generics
),
));
}
}
return Some(types);
};
let mut bindings = bindings.peekable();
let is_none = bindings.peek().is_none();
if is_none {
let mut types = self.get_schematic_types().collect::<Vec<_>>();
for matc in types.iter_mut() {
if matc.default.is_none() && self.ty.is_some() {
let ty = &self.ty;
let bound = &self.bound;
let ident = &matc.ident;
let generics = &matc.generics;
matc.default = Some((
token::Eq(Span::call_site()),
parse_quote!(
<#ty as #bound>::#ident #generics
),
));
}
}
Some(types)
} else {
for binding in bindings {
let Some(matc) = types
.iter_mut()
.find_map(|assoc| assoc.ident.eq(&binding.ident).then_some(assoc))
else {
panic!("Missing associated trait bindings")
};
if matc.default.is_none() {
matc.default = Some((binding.eq_token, binding.ty.clone()));
}
}
Some(types)
}
}
pub fn attach(&mut self, variant_sig: &VariantSig) {
let mut arms: BTreeMap<Ident, Vec<Arm>> = Default::default();
for item in self.schematic.items.iter() {
let TraitItem::Method(method) = item else {
continue;
};
let (method_name, parsed_arm) = variant_sig.parse_arm(method);
if let Some(arm_vec) = arms.get_mut(method_name) {
arm_vec.push(parsed_arm)
} else {
arms.insert(method_name.clone(), vec![parsed_arm]);
}
}
arms.into_iter().for_each(|(method_name, mut am)| {
if let Some(arm_vec) = self.methods.get_mut(&method_name) {
arm_vec.append(&mut am);
} else {
self.methods.insert(method_name, am);
}
})
}
pub fn get_sanatized_impl_path(&self) -> SynTraitBound {
let tb = self.bound.clone();
let mut tb: SynTraitBound = parse_quote!(#tb);
RemoveBoundBindings.visit_trait_bound_mut(&mut tb);
tb
}
}
impl<'bound> Blueprint<'bound> {
fn get_bound_bindings(&self) -> Option<impl Iterator<Item = &Binding>> {
if let Type::Path(path) = &self.bound.ty {
let path_segment = path.path.segments.last().unwrap();
match &path_segment.arguments {
syn::PathArguments::AngleBracketed(angle) => {
Some(angle.args.iter().filter_map(|arg| match arg {
syn::GenericArgument::Binding(binding) => Some(binding),
_ => None,
}))
}
_ => None,
}
} else {
None
}
}
fn get_bound_generics(&self) -> Option<impl Iterator<Item = &Type>> {
if let Type::Path(path) = &self.bound.ty {
let path_segment = path.path.segments.last().unwrap();
match &path_segment.arguments {
syn::PathArguments::AngleBracketed(angle) => {
Some(angle.args.iter().filter_map(|arg| match arg {
syn::GenericArgument::Type(ty) => Some(ty),
_ => None,
}))
}
_ => None,
}
} else {
None
}
}
fn get_schematic_generics(&self) -> impl Iterator<Item = &TypeParam> {
self.schematic
.generics
.params
.iter()
.filter_map(|param| match param {
syn::GenericParam::Type(ty) => Some(ty),
_ => None,
})
}
fn get_schematic_types(&self) -> impl Iterator<Item = TraitItemType> + '_ {
self.schematic.items.iter().filter_map(|item| match item {
TraitItem::Type(ty) => Some(ty.clone()),
_ => None,
})
}
fn get_schematic_methods(&self) -> impl Iterator<Item = TraitItemMethod> + '_ {
self.schematic.items.iter().filter_map(|item| match item {
TraitItem::Method(method) => Some(method.clone()),
_ => None,
})
}
}
impl<'bound> TryFrom<&'bound TraitBound> for Blueprint<'bound> {
type Error = syn::Error;
fn try_from(bound: &'bound TraitBound) -> Result<Self, Self::Error> {
let b_name = bound.get_ident();
if let Ok(schematic) = StandardTrait::try_from(&b_name) {
Ok(Self {
ty: None,
schematic: schematic.into(),
bound,
methods: Default::default(),
})
} else if let Some(Ok(schematic)) = T_SHM
.find(&b_name.to_string())
.as_ref()
.map(|result| parse_str::<ItemTrait>(result))
{
Ok(Self {
ty: None,
schematic: TraitSchematic(schematic),
bound,
methods: Default::default(),
})
} else {
Err(syn::Error::new_spanned(bound, trait_not_found(bound)))
}
}
}
fn trait_not_found(bound: &TraitBound) -> String {
format!("`{}` cannot be found. Make sure the trait is tagged with the `#[penum]` attribute, and is invoked before your enum.", bound.get_ident())
}
impl<'bound> BlueprintsMap<'bound> {
pub fn for_each_blueprint(&self, mut f: impl FnMut(&Blueprint)) {
let mut deduplicates: BTreeMap<UniqueHashId<Type>, Blueprint<'bound>> = Default::default();
for item in self.0.iter() {
for blueprint in item.1.iter() {
let bound = blueprint.bound;
let id: Type = parse_quote!(#bound);
let id_unique = UniqueHashId::new(&id);
if let Some(unique_entry) = deduplicates.get_mut(&id_unique) {
unique_entry
.methods
.extend(blueprint.methods.clone().into_iter());
} else {
deduplicates.insert(id_unique, blueprint.clone());
}
}
}
deduplicates.iter().for_each(|m| f(m.1))
}
pub fn find_and_attach(
&mut self,
id: &UniqueHashId<Type>,
variant_sig: &VariantSig,
ty: Option<&Type>,
) -> bool {
if let Some(bp_list) = self.get_mut(id) {
for blueprint in bp_list.iter_mut() {
blueprint.attach(variant_sig);
if ty.is_some() && blueprint.ty.is_none() {
blueprint.ty = Some(Box::from(unsafe { ty.unwrap_unchecked() }.clone()))
}
}
true
} else {
false
}
}
}
impl<'bound> Deref for BlueprintsMap<'bound> {
type Target = BTreeMap<UniqueHashId<Type>, Vec<Blueprint<'bound>>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'bound> DerefMut for BlueprintsMap<'bound> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.borrow_mut()
}
}
impl VisitMut for MonomorphizeFnSignature<'_> {
fn visit_generics_mut(&mut self, _: &mut syn::Generics) {}
fn visit_type_mut(&mut self, node: &mut syn::Type) {
if let Type::Path(typath) = node {
if let Some(&ty) = typath.path.get_ident().and_then(|ident| self.0.get(ident)) {
*node = ty.clone();
}
}
visit_type_mut(self, node);
}
}
impl VisitMut for MonomorphizeTraitBound<'_> {
fn visit_generics_mut(&mut self, _: &mut syn::Generics) {}
fn visit_type_mut(&mut self, node: &mut syn::Type) {
if let Type::Path(typath) = node {
if let Some(&ty) = typath.path.get_ident().and_then(|ident| self.0.get(ident)) {
*node = ty.clone();
}
}
visit_type_mut(self, node);
}
}
impl VisitMut for RemoveBoundBindings {
fn visit_angle_bracketed_generic_arguments_mut(
&mut self,
node: &mut syn::AngleBracketedGenericArguments,
) {
let mut rep_gas: Punctuated<GenericArgument, Token![,]> = Default::default();
let mut args = node.args.iter().peekable();
loop {
let (Some(gen), s) = (args.next(), args.peek()) else {
break;
};
if !matches!(gen, GenericArgument::Binding(_)) {
rep_gas.push_value(gen.clone());
if let Some(GenericArgument::Binding(_)) = s {
break;
} else {
rep_gas.push_punct(parse_quote!(,));
}
};
}
if args.count() != 0 {
node.args = rep_gas;
}
visit_angle_bracketed_generic_arguments_mut(self, node);
}
}