-
Notifications
You must be signed in to change notification settings - Fork 0
/
field.go
157 lines (140 loc) · 4.02 KB
/
field.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
package gosrc
import (
"fmt"
"go/ast"
"go/types"
"strings"
"github.com/fatih/structtag"
)
// Field represents one field of a structure.
type Field struct {
ast.Field
Struct *Struct
Names []*ast.Ident
Index uint
TypeValue types.TypeAndValue
}
// Field is a set of Field-s.
type Fields []*Field
// IsPointer returns true if the field has a pointer value
func (field Field) IsPointer() bool {
_, ok := TypeDeepest(field.TypeValue.Type).(*types.Pointer)
return ok
}
// IsSlice returns true if the field has a slice value
func (field Field) IsSlice() bool {
_, ok := TypeDeepest(field.TypeValue.Type).(*types.Slice)
return ok
}
// Methods returns all methods of the type of the value of the field.
func (field Field) Methods() Funcs {
namedType, ok := field.TypeValue.Type.(*types.Named)
if !ok {
return nil
}
return field.Struct.File.Package.Funcs().FindMethodsOf(namedType.Obj().Name())
}
// MethodByName returns the method of the type of the value of the field by
// its name (or nil of there is no such method)
func (field Field) MethodByName(methodName string) *Func {
funcs := field.Methods().FindByName(methodName)
switch len(funcs) {
case 0:
return nil
case 1:
return funcs[0]
default:
panic(fmt.Sprintf("found more than one method of field '%s' with the same name '%s': %d", field.Name(), methodName, len(funcs)))
}
}
// TypeElem returns Elem type of the value type.
//
// It will panic if IsPointer is false.
func (field Field) TypeElem() types.Type {
return TypeElem(field.TypeValue.Type)
}
// TagGet returns a value of the struct field tag with the specified key.
func (field Field) TagGet(key string) (string, bool) {
if field.Tag == nil {
return "", false
}
tags, err := structtag.Parse(strings.Trim(field.Tag.Value, "`"))
if err != nil {
return "", false
}
tag, err := tags.Get(key)
if err != nil {
return "", false
}
return tag.Value(), true
}
// TypeStdSize returns the size (in bytes) of the value for specified
// word size and max alignment. See more details in types.StdSizes.
func (field Field) TypeStdSize(wordSize, maxAlign int64) int64 {
return (&types.StdSizes{
WordSize: wordSize,
MaxAlign: maxAlign,
}).Sizeof(field.TypeValue.Type)
}
// Name returns the name of the field. For anonymous field it returns
// the name of the value type.
func (field Field) Name() string {
if len(field.Names) > 0 {
return field.Names[0].String()
}
// The field has no name, so we use Type name as the field name.
return itemTypeName(field.TypeValue.Type).Name
}
// ItemTypeName returns the name of the value type of an item referenced
// by the field. Few examples:
// * If field value type is uint64, then the returned value will be 'uint64'.
// * If field value type is []uint64, then the returned value will be 'uint64'.
// * If field value type is *uint64, then the returned value will be 'uint64'.
//
// This could be useful to find dependencies by names.
func (field Field) ItemTypeName() TypeNameValue {
return itemTypeName(field.TypeValue.Type)
}
// TypeNameValue is just a combination of a value type name and of a path
// where the type is defined.
type TypeNameValue struct {
Name string
Path string
}
func itemTypeName(typ types.Type) TypeNameValue {
for {
modified := true
switch casted := typ.(type) {
case interface{ Elem() types.Type }:
typ = casted.Elem()
case *types.Pointer:
typ = casted.Underlying()
default:
modified = false
}
if !modified {
break
}
}
// typ.String() is presented in forms "my/path/MyType" and "my/path.MyType"
parts := strings.Split(typ.String(), "/")
// lastPart is presented in forms "MyType" and "path.MyType"
lastPart := parts[len(parts)-1]
// subParts either ["MyType"] or ["path", "MyType"]
subParts := strings.Split(lastPart, ".")
switch len(subParts) {
case 1:
return TypeNameValue{
Name: subParts[0],
Path: "",
}
case 2:
parts[len(parts)-1] = subParts[0]
return TypeNameValue{
Name: subParts[1],
Path: strings.Join(parts, "/"),
}
default:
panic(fmt.Sprintf("do not know how to parse '%s'", typ.String()))
}
}