aboutsummaryrefslogtreecommitdiff
path: root/src/syn_utils.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/syn_utils.rs')
-rw-r--r--src/syn_utils.rs97
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
+}