use proc_macro2::{Ident, Span, TokenStream};
use syn::{GenericParam, Generics, Lifetime, LifetimeDef, TypeGenerics, TypeParam};
pub fn map_type_params<F, R>(generics: &Generics, mapper: F) -> Vec<R>
where
F: FnMut(&Ident) -> R,
{
generics
.params
.iter()
.filter_map(|param| match param {
GenericParam::Type(param) => Some(¶m.ident),
_ => None,
})
.map(mapper)
.collect()
}
pub fn map_lifetimes<F, R>(generics: &Generics, mapper: F) -> Vec<R>
where
F: FnMut(&Lifetime) -> R,
{
generics
.params
.iter()
.filter_map(|param| match param {
GenericParam::Lifetime(def) => Some(&def.lifetime),
_ => None,
})
.map(mapper)
.collect()
}
pub fn split_for_impl<'a>(
generics: &'a Generics,
extra_params: &[&str],
extra_lifetimes: &[&str],
) -> (TokenStream, TypeGenerics<'a>, TokenStream) {
let (_, ty_generics, where_clause) = generics.split_for_impl();
let ty_generics = ty_generics.clone();
let where_clause = where_clause
.map(|clause| {
if clause.predicates.empty_or_trailing() {
quote! { #clause }
} else {
quote! { #clause, }
}
})
.unwrap_or(quote! { where });
let mut generics = generics.clone();
extra_params
.into_iter()
.map(|param| GenericParam::from(TypeParam::from(Ident::new(param, Span::call_site()))))
.for_each(|param| {
if generics.params.iter().all(|p| *p != param) {
generics.params.insert(0, param)
}
});
extra_lifetimes
.into_iter()
.map(|lifetime| {
GenericParam::from(LifetimeDef::new(Lifetime::new(lifetime, Span::call_site())))
})
.for_each(|lifetime| {
if generics.params.iter().all(|p| *p != lifetime) {
generics.params.insert(0, lifetime)
}
});
let (impl_generics, ..) = generics.split_for_impl();
(quote! { #impl_generics }, ty_generics, where_clause)
}