use syn::{ punctuated::Punctuated, GenericArgument, Lifetime, Path, PathArguments, ReturnType, TraitBound, Type, TypeParamBound, }; #[allow(unused_variables)] pub trait TypeMatcher { fn match_type<'a>(&self, t: &'a Type) -> Option<&'a T> { None } fn match_path<'a>(&self, path: &'a Path) -> Option<&'a T> { None } } pub fn trait_bounds( bounds: &Punctuated, ) -> impl Iterator { bounds.iter().filter_map(|b| match b { TypeParamBound::Trait(t) => Some(t), TypeParamBound::Lifetime(_) => None, }) } pub fn lifetime_bounds( bounds: &Punctuated, ) -> impl Iterator { bounds.iter().filter_map(|b| match b { TypeParamBound::Trait(_) => None, TypeParamBound::Lifetime(l) => Some(l), }) } pub fn find_in_type<'a, T>(t: &'a Type, matcher: &dyn TypeMatcher) -> Option<&'a T> { if let Some(ret) = matcher.match_type(t) { return Some(ret); } match t { Type::Array(array) => find_in_type(&array.elem, matcher), Type::BareFn(fun) => { for input in &fun.inputs { if let Some(ret) = find_in_type(&input.ty, matcher) { return Some(ret); } } if let ReturnType::Type(_, t) = &fun.output { return find_in_type(t, matcher); } None } Type::Group(group) => find_in_type(&group.elem, matcher), Type::ImplTrait(impltrait) => { for bound in &impltrait.bounds { if let TypeParamBound::Trait(bound) = bound { if let Some(ret) = find_in_path(&bound.path, matcher) { return Some(ret); } } } None } Type::Infer(_) => None, Type::Macro(_) => None, Type::Paren(paren) => find_in_type(&paren.elem, matcher), Type::Path(path) => find_in_path(&path.path, matcher), Type::Ptr(ptr) => find_in_type(&ptr.elem, matcher), Type::Reference(reference) => find_in_type(&reference.elem, matcher), Type::Slice(slice) => find_in_type(&slice.elem, matcher), Type::TraitObject(traitobj) => { for bound in &traitobj.bounds { if let TypeParamBound::Trait(bound) = bound { if let Some(ret) = find_in_path(&bound.path, matcher) { return Some(ret); } } } None } Type::Tuple(tuple) => { for elem in &tuple.elems { if let Some(ret) = find_in_type(elem, matcher) { return Some(ret); } } None } _other => None, } } pub fn find_in_path<'a, T>(path: &'a Path, matcher: &dyn TypeMatcher) -> Option<&'a T> { if let Some(ret) = matcher.match_path(path) { return Some(ret); } for segment in &path.segments { if let PathArguments::AngleBracketed(args) = &segment.arguments { for arg in &args.args { if let GenericArgument::Type(t) = arg { if let Some(ret) = find_in_type(t, matcher) { return Some(ret); } } } } } None }