forked from huandu/go-sqlbuilder
-
Notifications
You must be signed in to change notification settings - Fork 0
/
structfields.go
148 lines (116 loc) · 3.22 KB
/
structfields.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
// Copyright 2018 Huan Du. All rights reserved.
// Copyright 2022 OOO SuperJob. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package sphinxql
import (
"reflect"
"sync"
)
type structFields struct {
fieldAlias map[string]string
taggedFields map[string][]string
quotedFields map[string]struct{}
omitEmptyFields map[string]omitEmptyTagMap
}
type structFieldsParser func() *structFields
func makeDefaultFieldsParser(t reflect.Type) structFieldsParser {
return makeFieldsParser(t, nil, true)
}
func makeCustomFieldsParser(t reflect.Type, mapper FieldMapperFunc) structFieldsParser {
return makeFieldsParser(t, mapper, false)
}
func makeFieldsParser(t reflect.Type, mapper FieldMapperFunc, useDefault bool) structFieldsParser {
var once sync.Once
sf := &structFields{
fieldAlias: map[string]string{},
taggedFields: map[string][]string{},
quotedFields: map[string]struct{}{},
omitEmptyFields: map[string]omitEmptyTagMap{},
}
return func() *structFields {
once.Do(func() {
if useDefault {
mapper = DefaultFieldMapper
}
sf.parse(t, mapper, "")
})
return sf
}
}
func (sf *structFields) parse(t reflect.Type, mapper FieldMapperFunc, prefix string) {
l := t.NumField()
var anonymous []reflect.StructField
for i := 0; i < l; i++ {
field := t.Field(i)
if field.Anonymous {
ft := field.Type
// If field is an anonymous struct or pointer to struct, parse it later.
if k := ft.Kind(); k == reflect.Struct || (k == reflect.Ptr && ft.Elem().Kind() == reflect.Struct) {
anonymous = append(anonymous, field)
continue
}
}
// Parse DBTag.
dbtag := field.Tag.Get(DBTag)
alias := dbtag
if dbtag == "-" {
continue
}
if dbtag == "" {
if mapper == nil {
alias = field.Name
} else {
alias = mapper(field.Name)
}
}
// The alias name has been used by another field.
// This field is shadowed.
if _, ok := sf.fieldAlias[alias]; ok {
continue
}
sf.fieldAlias[alias] = field.Name
// Parse FieldTag.
fieldtag := field.Tag.Get(FieldTag)
tags := splitTokens(fieldtag)
for _, t := range tags {
if t != "" {
sf.taggedFields[t] = append(sf.taggedFields[t], alias)
}
}
sf.taggedFields[""] = append(sf.taggedFields[""], alias)
// Parse FieldOpt.
fieldopt := field.Tag.Get(FieldOpt)
opts := optRegex.FindAllString(fieldopt, -1)
for _, opt := range opts {
optMap := getOptMatchedMap(opt)
switch optMap[optName] {
case fieldOptOmitEmpty:
tags := getTagsFromOptParams(optMap[optParams])
sf.appendOmitEmptyFieldsTags(alias, tags...)
case fieldOptWithQuote:
sf.quotedFields[alias] = struct{}{}
}
}
}
for _, field := range anonymous {
ft := dereferencedType(field.Type)
sf.parse(ft, mapper, prefix+field.Name+".")
}
}
func (sf *structFields) appendOmitEmptyFieldsTags(alias string, tags ...string) {
if sf.omitEmptyFields[alias] == nil {
sf.omitEmptyFields[alias] = omitEmptyTagMap{}
}
for _, tag := range tags {
sf.omitEmptyFields[alias][tag] = struct{}{}
}
}
type omitEmptyTagMap map[string]struct{}
func (sm omitEmptyTagMap) containsAny(tags ...string) (res bool) {
for _, tag := range tags {
if _, res = sm[tag]; res {
return
}
}
return
}