use std::collections::HashMap;
use syn::{GenericParam, Generics, Ident, Lifetime, TypeParamBound, WherePredicate};
use crate::extractor::Extractor;
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum Param {
Lifetime(Lifetime),
Ident(Ident),
}
impl From<Ident> for Param {
fn from(value: Ident) -> Self {
Param::Ident(value)
}
}
impl From<Lifetime> for Param {
fn from(value: Lifetime) -> Self {
Param::Lifetime(value)
}
}
impl Param {
pub fn find<'b>(
&self,
generics: &'b Generics,
) -> (Option<&'b GenericParam>, Option<&'b WherePredicate>) {
match self {
Param::Lifetime(lt) => find_lt(lt, generics),
Param::Ident(ty) => find_ident(ty, generics),
}
}
pub fn find_relevant(&self, bound_map: &HashMap<Param, Vec<TypeParamBound>>) -> Vec<Param> {
match bound_map.get(self) {
Some(bounds) => bounds
.iter()
.flat_map(|bound| match bound {
TypeParamBound::Trait(tr) => {
tr.path
.get_ident()
.into_iter()
.cloned()
.flat_map(|ident| Param::from(ident).find_relevant(bound_map))
.collect()
}
TypeParamBound::Lifetime(lifetime) => {
Param::from(lifetime.clone()).find_relevant(bound_map)
}
})
.chain([self.clone()].into_iter())
.collect(),
None => Vec::new(),
}
}
}
fn find_lt<'a>(
lt: &Lifetime,
generics: &'a Generics,
) -> (Option<&'a GenericParam>, Option<&'a WherePredicate>) {
let generic_param = generics.params.iter().find(|p| match p {
GenericParam::Lifetime(lt_def) => <_def.lifetime == lt,
_ => false,
});
let predicate = generics
.where_clause
.iter()
.flat_map(|wh| wh.predicates.iter())
.find(|pred| match pred {
WherePredicate::Lifetime(pred_lt) => &pred_lt.lifetime == lt,
_ => false,
});
(generic_param, predicate)
}
fn find_ident<'a>(
ident: &Ident,
generics: &'a Generics,
) -> (Option<&'a GenericParam>, Option<&'a WherePredicate>) {
let generic_param = generics.params.iter().find(|p| match p {
GenericParam::Type(ty_param) => &ty_param.ident == ident,
_ => false,
});
let predicate = generics
.where_clause
.iter()
.flat_map(|wh| wh.predicates.iter())
.find(|pred| match pred {
WherePredicate::Type(pred_ty) => pred_ty
.bounded_ty
.extract_idents()
.iter()
.any(|id| id == ident),
_ => false,
});
(generic_param, predicate)
}