forked from utrack/pontoon
/
handler.go
106 lines (90 loc) · 2.25 KB
/
handler.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
package main
import (
"go/ast"
"go/types"
"github.com/pkg/errors"
"golang.org/x/tools/go/ast/astutil"
)
func (b builder) getHandleDesc(fnIdent *ast.Ident, ms *types.MethodSet) (*hdlTypesDesc, error) {
var sel *types.Selection
for i := 0; i < ms.Len(); i++ {
s := ms.At(i)
if s.Obj().Name() == fnIdent.Name {
sel = s
break
}
}
if sel == nil {
return nil, errors.Errorf("handler func '%v' not found", fnIdent.Name)
}
f, _ := b.astFindFile(sel.Obj().Pos())
sig := sel.Type().(*types.Signature)
var inType *typeDesc
var outType *typeDesc
var hasResponseWriter bool
for i := 0; i < sig.Params().Len(); i++ {
p := sig.Params().At(i)
namedType := p.Type()
if namedType.String() == "*net/http.Request" {
continue
}
if namedType.String() == "net/http.ResponseWriter" {
hasResponseWriter = true
continue
}
if inType != nil {
return nil, errors.New("handler has more than one request type")
}
var err error
inType, err = b.rootStructDesc(namedType)
if err != nil {
return nil, errors.Wrapf(err, "converting input type '%v' to type description", namedType.String())
}
if inType.isAny {
return nil, errors.Errorf("input type '%v' cannot be an interface", namedType.String())
}
}
if !hasResponseWriter {
for i := 0; i < sig.Results().Len(); i++ {
r := sig.Results().At(i)
t := r.Type()
if t.String() == "error" {
continue
}
if outType != nil {
return nil, errors.New("handler has more than one response type")
}
var err error
outType, err = b.rootStructDesc(t)
if err != nil {
return nil, errors.Wrapf(err, "converting result type '%v' to type description", t)
}
}
}
ret := &hdlTypesDesc{
hasResponseWriter: hasResponseWriter,
inType: inType,
outType: outType,
}
if f != nil {
pathdesc, _ := astutil.PathEnclosingInterval(f, sel.Obj().Pos(), sel.Obj().Pos())
for _, n := range pathdesc {
fd, ok := n.(*ast.FuncDecl)
if !ok {
continue
}
if fd.Name == nil ||
fd.Name.Name != fnIdent.Name {
continue
}
ret.description = fd.Doc.Text()
}
}
return ret, nil
}
func (b *builder) rootStructDesc(t types.Type) (*typeDesc, error) {
if p, ok := t.(*types.Pointer); ok {
t = p.Elem()
}
return b.getTypeDescCached(t)
}