aboutsummaryrefslogtreecommitdiff
path: root/src/syn_utils.rs
blob: 458818646d08fea42ac99aad47b21836ce0754f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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
}