-
Notifications
You must be signed in to change notification settings - Fork 1
/
provider.go
134 lines (123 loc) · 3.3 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
package parse
import (
"go/ast"
)
func searchProvider(funcdecl *ast.FuncDecl, structs map[string]structDecl, packageName string, imports map[string]Import) (name string, deps map[string][]Dep) {
if funcdecl.Name.Name[:3] != "New" {
return name, deps
}
name = searchDependencyName(funcdecl)
s, ok := structs[packageName+"."+name]
if !ok {
return "", nil
}
deps = searchDependencies(funcdecl, packageName, imports)
searchDependenciesAssignment(funcdecl, deps, s)
return name, deps
}
// searchDependencyName search the created dependency as the first variable returned.
func searchDependencyName(funcdecl *ast.FuncDecl) string {
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]Import) (deps map[string][]Dep) {
deps = map[string][]Dep{}
for _, param := range funcdecl.Type.Params.List {
packageName, serviceName := getDepID(param.Type)
imp := imports[packageName]
external := imp.External
if packageName == "" {
packageName = name
}
if serviceName == "" {
continue
}
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 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 structDecl) {
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 structDecl) {
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[value.Name].methods
}
}
}
}