-
Notifications
You must be signed in to change notification settings - Fork 1
/
provider.go
171 lines (156 loc) · 4.17 KB
/
provider.go
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
package parse
import (
"go/ast"
"go/types"
"github.com/emilien-puget/go-dependency-graph/pkg/parse/struct_decl"
)
// dep represent one dependency injected.
type dep struct {
PackageName string
DependencyName string
VarName string
Funcs []string
External bool
}
func searchProvider(funcdecl *ast.FuncDecl, packageName string, imports map[string]importDecl, typesInfo *types.Info, t map[string]map[string]*struct_decl.Decl) (name string, deps map[string][]dep, decl *struct_decl.Decl) {
if len(funcdecl.Name.Name) < 3 || funcdecl.Name.Name[:3] != "New" {
return "", nil, nil
}
name = searchDependencyName(funcdecl)
decl, ok := t[packageName][name]
if !ok {
return "", nil, nil
}
deps = searchDependencies(funcdecl, packageName, imports, typesInfo)
searchDependenciesAssignment(funcdecl, deps, decl)
return name, deps, decl
}
// searchDependencyName search the created dependency as the first variable returned.
func searchDependencyName(funcdecl *ast.FuncDecl) string {
results := funcdecl.Type.Results
if results == nil {
return ""
}
switch t := funcdecl.Type.Results.List[0].Type.(type) { // get the type of the dependency.
case *ast.StarExpr: // dependency returned as a pointer.
ident, ok := t.X.(*ast.Ident)
if !ok {
return ""
}
return ident.Name
case *ast.Ident: // dependency returned as a value.
return t.Name
}
return ""
}
// searchDependencies returns the dependency found in the provider type declaration.
func searchDependencies(funcdecl *ast.FuncDecl, name string, imports map[string]importDecl, info *types.Info) (deps map[string][]dep) {
deps = map[string][]dep{}
for _, param := range funcdecl.Type.Params.List {
if !checkDepsMethods(info.TypeOf(param.Type)) { // ignore dependencies without methods
continue
}
packageName, serviceName := getDepID(param.Type)
if serviceName == "" {
continue
}
imp := imports[packageName]
external := imp.External
if packageName == "" {
packageName = name
} else {
packageName = imp.Path
}
varName := ""
for _, name := range param.Names {
varName = name.String()
}
if external {
packageName = imp.Path
}
deps[varName] = append(deps[varName], dep{
VarName: varName,
PackageName: packageName,
DependencyName: serviceName,
External: external,
})
}
return deps
}
func checkDepsMethods(t types.Type) bool {
ptrType, ok := t.(*types.Pointer)
if ok {
t = ptrType.Elem()
}
namedType, ok := t.(*types.Named)
if !ok {
return false
}
if namedType.NumMethods() > 0 {
return true
}
return false
}
func getDepID(dep ast.Expr) (packageName, serviceName string) {
if depStar, ok := dep.(*ast.StarExpr); ok {
dep = depStar.X
}
switch p := dep.(type) {
case *ast.SelectorExpr:
ident, ok := p.X.(*ast.Ident)
if !ok {
return "", ""
}
return ident.Name, p.Sel.Name
case *ast.Ident:
return "", p.Name
}
return "", ""
}
// searchDependenciesAssignment parse the provider function to search for a return.
// this return is then parsed to look for injected functions to complete the deps found in the previous step.
func searchDependenciesAssignment(funcdecl *ast.FuncDecl, deps map[string][]dep, s *struct_decl.Decl) {
if funcdecl.Body != nil {
for _, stmt := range funcdecl.Body.List {
retStmt, ok := stmt.(*ast.ReturnStmt)
if !ok {
continue
}
uExpr, ok := retStmt.Results[0].(*ast.UnaryExpr)
if !ok {
continue
}
cpLit, ok := uExpr.X.(*ast.CompositeLit)
if !ok {
continue
}
for _, elt := range cpLit.Elts {
kvExpr, ok := elt.(*ast.KeyValueExpr)
if !ok {
continue
}
setDepsFunc(kvExpr, deps, s)
}
}
}
}
func setDepsFunc(kvExpr *ast.KeyValueExpr, deps map[string][]dep, s *struct_decl.Decl) {
switch value := kvExpr.Value.(type) {
case *ast.SelectorExpr:
x, ok := value.X.(*ast.Ident)
if !ok {
return
}
if _, ok := deps[x.String()]; ok {
for i := range deps[x.String()] {
deps[x.String()][i].Funcs = append(deps[x.String()][i].Funcs, value.Sel.String())
}
}
case *ast.Ident:
if _, ok := deps[value.Name]; ok {
for i := range deps[value.Name] {
deps[value.Name][i].Funcs = s.Fields[kvExpr.Key.(*ast.Ident).Name].Methods
}
}
}
}