-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfield.go
More file actions
124 lines (100 loc) · 2.76 KB
/
Copy pathfield.go
File metadata and controls
124 lines (100 loc) · 2.76 KB
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
package structify
import (
"errors"
"fmt"
"reflect"
)
// ErrNotExported is returned when trying to set an unexported field.
var ErrNotExported = errors.New("structify: field is not exported")
// ErrNotSettable is returned when a field cannot be set.
var ErrNotSettable = errors.New("structify: field is not settable")
// ErrTypeMismatch is returned when the value type doesn't match the field type.
var ErrTypeMismatch = errors.New("structify: type mismatch")
// Field represents a single struct field.
type Field struct {
value reflect.Value
field reflect.StructField
tagName string
tagOpts tagOptions
}
// Name returns the field name.
func (f *Field) Name() string {
return f.field.Name
}
// Value returns the field value as any.
func (f *Field) Value() any {
return f.value.Interface()
}
// Kind returns the reflect.Kind of the field.
func (f *Field) Kind() reflect.Kind {
return f.value.Kind()
}
// Tag returns the value of the struct tag for the given key.
func (f *Field) Tag(key string) string {
return f.field.Tag.Get(key)
}
// IsZero returns true if the field value is the zero value for its type.
func (f *Field) IsZero() bool {
return f.value.IsZero()
}
// IsExported returns true if the field is exported.
func (f *Field) IsExported() bool {
return f.field.IsExported()
}
// IsEmbedded returns true if the field is an embedded (anonymous) field.
func (f *Field) IsEmbedded() bool {
return f.field.Anonymous
}
// Set sets the field value. The field must be exported and settable.
// Returns an error if the field is unexported, not settable, or the types don't match.
func (f *Field) Set(val any) error {
if !f.IsExported() {
return ErrNotExported
}
if !f.value.CanSet() {
return ErrNotSettable
}
v := reflect.ValueOf(val)
if !v.Type().AssignableTo(f.value.Type()) {
return fmt.Errorf("%w: cannot assign %s to %s", ErrTypeMismatch, v.Type(), f.value.Type())
}
f.value.Set(v)
return nil
}
// Fields returns the nested struct fields if this field is a struct.
// Returns nil for non-struct fields. Handles nil pointer fields gracefully.
func (f *Field) Fields() []*Field {
val := f.value
kind := val.Kind()
if kind == reflect.Ptr {
if val.IsNil() {
return nil
}
val = val.Elem()
kind = val.Kind()
}
if kind != reflect.Struct {
return nil
}
return getFields(val, f.tagName)
}
// getFields extracts fields from a struct reflect.Value.
func getFields(val reflect.Value, tagName string) []*Field {
t := val.Type()
fields := make([]*Field, 0, t.NumField())
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
fv := val.Field(i)
opts := parseTag(sf.Tag.Get(tagName))
if opts.skip {
continue
}
fields = append(fields, &Field{
value: fv,
field: sf,
tagName: tagName,
tagOpts: opts,
})
}
return fields
}