/
struct.go
241 lines (214 loc) · 5.66 KB
/
struct.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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
package types
import (
"strconv"
"strings"
)
// Compile type check if *Struct implements Type interface.
var _ Type = (*Struct)(nil)
// Struct is the struct type reflection.
type Struct struct {
Pkg *Package
Comment string
TypeName string
Fields []StructField
Methods []Function
}
// Implements checks if given structure implements provided interface.
func (s *Struct) Implements(interfaceType *Interface, pointer bool) bool {
return implements(interfaceType, s, pointer)
}
// Name implements Type interface.
func (s *Struct) Name(identifier bool, packageContext string) string {
if identifier && packageContext != s.Pkg.Path {
if i := s.Pkg.Identifier; i != "" {
return i + "." + s.TypeName
}
}
return s.TypeName
}
// FullName implements Type interface.
func (s *Struct) FullName() string {
return s.Pkg.Path + "/" + s.TypeName
}
// Package implements Type interface.
func (s *Struct) Package() *Package {
return s.Pkg
}
// Kind implements Type interface.
func (s *Struct) Kind() Kind {
return KindStruct
}
// Elem implements Type interface.
func (s *Struct) Elem() Type {
return nil
}
// KindString implements Type interface.
func (s *Struct) String() string {
return s.Name(true, "")
}
// Zero implements Type interface.
func (s *Struct) Zero(identified bool, packageContext string) string {
return s.Name(identified, packageContext) + "{}"
}
// Equal implements Type interface.
func (s *Struct) Equal(another Type) bool {
st, ok := another.(*Struct)
if !ok {
return false
}
return st.Pkg == s.Pkg && st.TypeName == s.TypeName
}
func (s *Struct) getMethods() []Function {
return s.Methods
}
// StructField is a structure field model.
type StructField struct {
Name string
Comment string
Type Type
Tag StructTag
Index []int
Embedded bool
Anonymous bool
}
// KindString implements fmt.Stringer interface.
func (s StructField) String() string {
return s.Name + "\t" + s.Type.String() + "\t`" + string(s.Tag) + "`"
}
// A StructTag is the tag string in a struct field.
//
// By convention, tag strings are a concatenation of
// optionally space-separated key:"value" pairs.
// Each key is a non-empty string consisting of non-control
// characters other than space (U+0020 ' '), quote (U+0022 '"'),
// and colon (U+003A ':'). Each value is quoted using U+0022 '"'
// characters and Go string literal syntax.
type StructTag string
// StructTagTuple is a tuple key,value for the struct tag.
type StructTagTuple struct {
Key, Value string
}
// StructTagTuples is the slice alias over the the structTag key, value tuples.
// It is used to recreate the StructTag.
type StructTagTuples []StructTagTuple
// Join joins the structTag tuples and creates a single StructTag.
func (s StructTagTuples) Join() StructTag {
var sb strings.Builder
for i := range s {
sb.WriteString(s[i].Key)
sb.WriteRune(':')
sb.WriteRune('"')
sb.WriteString(s[i].Value)
sb.WriteRune('"')
if i != len(s)-1 {
sb.WriteRune(' ')
}
}
return StructTag(sb.String())
}
// Split splits up the struct tag into key, value tuples.
func (tag StructTag) Split() (tuples StructTagTuples) {
st := tag
for st != "" {
// Skip leading space.
i := 0
for i < len(st) && st[i] == ' ' {
i++
}
st = st[i:]
if st == "" {
break
}
i = 0
for i < len(st) && st[i] > ' ' && st[i] != ':' && st[i] != '"' && st[i] != 0x7f {
i++
}
if i == 0 || i+1 >= len(st) || st[i] != ':' || st[i+1] != '"' {
break
}
name := string(st[:i])
st = st[i+1:]
// Scan quoted string to find value.
i = 1
for i < len(st) && st[i] != '"' {
if st[i] == '\\' {
i++
}
i++
}
if i >= len(st) {
break
}
quotedValue := string(st[:i+1])
st = st[i+1:]
value, err := strconv.Unquote(quotedValue)
if err != nil {
break
}
tuples = append(tuples, StructTagTuple{Key: name, Value: value})
}
return tuples
}
// Get returns the value associated with key in the tag string.
// If there is no such key in the tag, Get returns the empty string.
// If the tag does not have the conventional format, the value
// returned by Get is unspecified. To determine whether a tag is
// explicitly set to the empty string, use Lookup.
func (tag StructTag) Get(key string) string {
v, _ := tag.Lookup(key)
return v
}
// Lookup returns the value associated with key in the tag string.
// If the key is present in the tag the value (which may be empty)
// is returned. Otherwise the returned value will be the empty string.
// The ok return value reports whether the value was explicitly set in
// the tag string. If the tag does not have the conventional format,
// the value returned by Lookup is unspecified.
func (tag StructTag) Lookup(key string) (value string, ok bool) {
st := tag
for st != "" {
// Skip leading space.
i := 0
for i < len(st) && st[i] == ' ' {
i++
}
st = st[i:]
if st == "" {
break
}
// Scan to colon. A space, a quote or a control character is a syntax error.
// Strictly speaking, control chars include the range [0x7f, 0x9f], not just
// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
// as it is simpler to inspect the st's bytes than the st's runes.
i = 0
for i < len(st) && st[i] > ' ' && st[i] != ':' && st[i] != '"' && st[i] != 0x7f {
i++
}
if i == 0 || i+1 >= len(st) || st[i] != ':' || st[i+1] != '"' {
break
}
name := string(st[:i])
st = st[i+1:]
// Scan quoted string to find value.
i = 1
for i < len(st) && st[i] != '"' {
if st[i] == '\\' {
i++
}
i++
}
if i >= len(st) {
break
}
qvalue := string(st[:i+1])
st = st[i+1:]
if key == name {
value, err := strconv.Unquote(qvalue)
if err != nil {
break
}
return value, true
}
}
return "", false
}