-
Notifications
You must be signed in to change notification settings - Fork 4
/
visitor.go
87 lines (71 loc) · 1.85 KB
/
visitor.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
package glue
import (
"go/ast"
"go/types"
"github.com/segmentio/glue/provider"
"golang.org/x/tools/go/loader"
)
// Visitor traverses a Go package's AST, visits declarations that satisfy the
// structure of an RPC service, and extracts their RPC methods.
type Visitor struct {
pkg *loader.PackageInfo
methods map[string][]*types.Func
provider provider.Provider
target string
}
// VisitorConfig is used to create a Visitor.
type VisitorConfig struct {
// Pkg contains metadata for the target package.
Pkg *loader.PackageInfo
// Provider determines which RPC methods are suitable.
Provider provider.Provider
// Declaration is the name of the target RPC declaration (method receiver).
Declaration string
}
// NewVisitor creates a Visitor.
func NewVisitor(cfg VisitorConfig) *Visitor {
return &Visitor{
pkg: cfg.Pkg,
provider: cfg.Provider,
methods: map[string][]*types.Func{},
target: cfg.Declaration,
}
}
// Go starts Visitor's trip around the supplied package. Upon return, it
// sends a mapping of receiver identifiers to RPC methods.
func (p *Visitor) Go() map[string][]*types.Func {
for _, file := range p.pkg.Files {
ast.Walk(p, file)
}
return p.methods
}
// Visit extracts functions from RPC declarations. It satisfies go/ast.Visitor.
func (p *Visitor) Visit(node ast.Node) ast.Visitor {
switch n := node.(type) {
case nil:
return nil
case *ast.TypeSpec:
p.visitType(n)
}
return p
}
func (p *Visitor) visitType(ts *ast.TypeSpec) {
obj := p.pkg.Info.ObjectOf(ts.Name)
if obj == nil {
return
}
namedType, ok := obj.Type().(*types.Named)
if !ok {
return
}
if obj.Name() != p.target {
return
}
for i := 0; i < namedType.NumMethods(); i++ {
method := namedType.Method(i)
if p.provider.IsSuitableMethod(method) {
recv := namedType.Obj().Name()
p.methods[recv] = append(p.methods[recv], method)
}
}
}