forked from fatih/structs
-
Notifications
You must be signed in to change notification settings - Fork 3
/
field.go
163 lines (141 loc) · 4.32 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
158
159
160
161
162
163
package structs
import (
"errors"
"fmt"
"reflect"
)
var (
errNotExported = errors.New("field is not exported")
errNotSettable = errors.New("field is not settable")
)
// Field represents a single struct field that encapsulates high level
// functions around the field.
type Field struct {
value reflect.Value
field reflect.StructField
}
// Tag returns the value associated with key in the tag string. If there is no
// such key in the tag, Tag returns the empty string.
func (f *Field) Tag(key string) string {
return f.field.Tag.Get(key)
}
// Value returns the underlying value of of the field. It panics if the field
// is not exported.
func (f *Field) Value() interface{} {
return f.value.Interface()
}
// IsEmbedded returns true if the given field is an anonymous field (embedded)
func (f *Field) IsEmbedded() bool {
return f.field.Anonymous
}
// IsExported returns true if the given field is exported.
func (f *Field) IsExported() bool {
return f.field.PkgPath == ""
}
// IsZero returns true if the given field is not initalized (has a zero value).
// It panics if the field is not exported.
func (f *Field) IsZero() bool {
zero := reflect.Zero(f.value.Type()).Interface()
current := f.Value()
return reflect.DeepEqual(current, zero)
}
// Name returns the name of the given field
func (f *Field) Name() string {
return f.field.Name
}
// Kind returns the fields kind, such as "string", "map", "bool", etc ..
func (f *Field) Kind() reflect.Kind {
return f.value.Kind()
}
func (f *Field) checkForSet(v reflect.Value, val interface{}) (reflect.Value, error) {
var given reflect.Value
if !f.IsExported() {
return given, errNotExported
}
// do we get here? not sure...
if !v.CanSet() {
return given, errNotSettable
}
given = reflect.ValueOf(val)
if given.Kind() == reflect.Ptr && v.Kind() != reflect.Ptr {
given = given.Elem()
}
return given, nil
}
// Set sets the field to given value v. It retuns an error if the field is not
// settable (not addresable or not exported) or if the given value's type
// doesn't match the fields type.
func (f *Field) Set(val interface{}) error {
// needed to make the field settable
given, err := f.checkForSet(f.value, val)
if err != nil {
return err
}
if f.value.Kind() != given.Kind() {
return fmt.Errorf("wrong kind: %s want: %s", given.Kind(), f.value.Kind())
}
f.value.Set(given)
return nil
}
// SetInt sets the field underlying value to val. It panics if the field is not a Kind is not Int, Int8, Int16, Int32, or Int64, or if CanSet() is false.
func (f *Field) SetInt(val int64) error {
_, err := f.checkForSet(f.value, val)
if err != nil {
return err
}
f.value.SetInt(val)
return nil
}
// SetFloat sets the field underlying value to val. It panics if the field is not a Kind is not Float32 or Float64, or if CanSet() is false.
func (f *Field) SetFloat(val float64) error {
_, err := f.checkForSet(f.value, val)
if err != nil {
return err
}
f.value.SetFloat(val)
return nil
}
//SetUint sets the field underlying value to val. It panics if the field is not a Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64, or if CanSet() is false.
func (f *Field) SetUint(val uint64) error {
_, err := f.checkForSet(f.value, val)
if err != nil {
return err
}
f.value.SetUint(val)
return nil
}
// Fields returns a slice of Fields. This is particular handy to get the fields
// of a nested struct . A struct tag with the content of "-" ignores the
// checking of that particular field. Example:
//
// // Field is ignored by this package.
// Field *http.Request `structs:"-"`
//
// It panics if field is not exported or if field's kind is not struct
func (f *Field) Fields() []*Field {
return getFields(f.value)
}
// Field returns the field from a nested struct. It panics if the nested struct
// is not exported or if the field was not found.
func (f *Field) Field(name string) *Field {
field, ok := f.FieldOk(name)
if !ok {
panic("field not found")
}
return field
}
// Field returns the field from a nested struct. The boolean returns true if
// the field was found. It panics if the nested struct is not exported or if
// the field was not found.
func (f *Field) FieldOk(name string) (*Field, bool) {
v := strctVal(f.value.Interface())
t := v.Type()
field, ok := t.FieldByName(name)
if !ok {
return nil, false
}
return &Field{
field: field,
value: v.FieldByName(name),
}, true
}