-
Notifications
You must be signed in to change notification settings - Fork 1
/
marker.go
181 lines (167 loc) · 4.29 KB
/
marker.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
package structology
import (
"fmt"
"github.com/viant/xunsafe"
"reflect"
"unsafe"
)
// Marker field set marker
type Marker struct {
t reflect.Type
holder *xunsafe.Field
fields []*xunsafe.Field
index map[string]int //marker field post
noStrict bool
}
// Type returns marker reflect type
func (p *Marker) Type() reflect.Type {
if p.holder == nil {
return nil
}
return p.holder.Type
}
// IsFieldSet returns true if filed has been set
func (p *Marker) IsFieldSet(ptr unsafe.Pointer, name string) bool {
idx := p.Index(name)
if idx == -1 { //no info we assume it's set
return true
}
return p.IsSet(ptr, idx)
}
// Index returns mapped field index or -1
func (p *Marker) Index(name string) int {
if p == nil || len(p.index) == 0 {
return -1
}
pos, ok := p.index[name]
if !ok {
return -1
}
return pos
}
// SetAll sets all marker field with supplied flag
func (p *Marker) SetAll(ptr unsafe.Pointer, flag bool) error {
if !p.CanUseHolder(ptr) {
return fmt.Errorf("failed to set all due to holder was empty")
}
markerPtr := p.holder.ValuePointer(ptr)
for _, field := range p.fields {
if field == nil {
continue
}
field.SetBool(markerPtr, flag)
}
return nil
}
func (p *Marker) CanUseHolder(ptr unsafe.Pointer) bool {
if p.holder == nil || p.holder.IsNil(ptr) {
return false
}
return true
}
// Set sets field marker
func (p *Marker) Set(ptr unsafe.Pointer, index int, flag bool) error {
if !p.CanUseHolder(ptr) {
return fmt.Errorf("holder was empty")
}
markerPtr := p.holder.ValuePointer(ptr)
if index >= len(p.fields) || p.fields[index] == nil {
return fmt.Errorf("field at index %v was missing in set marker", index)
}
p.fields[index].SetBool(markerPtr, flag)
return nil
}
// IsSet returns true if field has been set
func (p *Marker) IsSet(ptr unsafe.Pointer, index int) bool {
if p == nil || p.holder == nil || p.holder.IsNil(ptr) {
return true //we do not have field presence provider so we assume all fields are set
}
if p.holder.IsNil(ptr) {
return true //holder is nil
}
return p.has(ptr, index)
}
func (p *Marker) EnsureHolder(ptr unsafe.Pointer) {
if !p.holder.IsNil(ptr) {
return
}
isPtr := p.holder.Type.Kind() == reflect.Ptr
var value interface{}
if isPtr {
value = reflect.New(p.holder.Type.Elem()).Interface()
} else {
value = reflect.New(p.holder.Type).Elem().Interface()
}
p.holder.SetValue(ptr, value)
}
// Has checks if filed value was flagged as set
func (p *Marker) has(ptr unsafe.Pointer, index int) bool {
markerPtr := p.holder.ValuePointer(ptr)
if index >= len(p.fields) || p.fields[index] == nil {
return false
}
return p.fields[index].Bool(markerPtr)
}
// Init initialises field set marker
func (p *Marker) init() error {
if p.holder == nil {
typeName := ""
if p.t != nil {
typeName = p.t.String()
}
return fmt.Errorf("holder was empty for %s", typeName)
}
if len(p.index) == 0 {
return fmt.Errorf("struct has no markable fields")
}
if holder := p.holder; holder != nil {
p.fields = make([]*xunsafe.Field, len(p.index))
holderType := ensureStruct(holder.Type)
for i := 0; i < holderType.NumField(); i++ {
markerField := holderType.Field(i)
pos, ok := p.index[markerField.Name]
if !ok {
if p.noStrict {
continue
}
return fmt.Errorf("marker filed: '%v' does not have corresponding struct field", markerField.Name)
}
p.fields[pos] = xunsafe.NewField(markerField)
}
}
return nil
}
// NewMarker returns new struct field set marker
func NewMarker(t reflect.Type, opts ...Option) (*Marker, error) {
if t = ensureStruct(t); t == nil {
return nil, fmt.Errorf("supplied type is not struct")
}
numFiled := t.NumField()
var result = &Marker{t: t, fields: make([]*xunsafe.Field, numFiled), index: make(map[string]int, numFiled)}
Options(opts).Apply(result)
hasIndex := len(result.index) > 0
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
if !hasIndex {
result.index[field.Name] = field.Index[0]
}
if IsSetMarker(field.Tag) {
result.holder = xunsafe.NewField(field)
}
}
return result, result.init()
}
// HasSetMarker returns true if struct has set marker
func HasSetMarker(t reflect.Type) bool {
t = ensureStruct(t)
if t == nil {
return false
}
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
if IsSetMarker(field.Tag) {
return true
}
}
return false
}