diff options
author | Martin Fischer <martin@push-f.com> | 2021-11-15 10:29:52 +0100 |
---|---|---|
committer | Martin Fischer <martin@push-f.com> | 2021-11-18 23:36:01 +0100 |
commit | 2a8a0601afcb82d90d0766db5a954b70b10f856d (patch) | |
tree | 0271062335d450e151598d4ad9aa327ffa0dfaea /src/syn_utils.rs |
publishv0.1.0
Diffstat (limited to 'src/syn_utils.rs')
-rw-r--r-- | src/syn_utils.rs | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/src/syn_utils.rs b/src/syn_utils.rs new file mode 100644 index 0000000..4588186 --- /dev/null +++ b/src/syn_utils.rs @@ -0,0 +1,97 @@ +use syn::{ + punctuated::Punctuated, GenericArgument, Path, PathArguments, ReturnType, TraitBound, Type, + TypeParamBound, +}; + +pub trait TypeMatcher<T> { + 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<T>( + bounds: &Punctuated<TypeParamBound, T>, +) -> impl Iterator<Item = &TraitBound> { + bounds.iter().filter_map(|b| match b { + TypeParamBound::Trait(t) => Some(t), + TypeParamBound::Lifetime(_) => None, + }) +} + +pub fn find_in_type<'a, T>(t: &'a Type, matcher: &dyn TypeMatcher<T>) -> 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<T>) -> 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 +} |