-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstructify.go
More file actions
107 lines (92 loc) · 2.73 KB
/
structify.go
File metadata and controls
107 lines (92 loc) · 2.73 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
package structify
import (
"errors"
"fmt"
"reflect"
)
// ErrNotStruct is returned when a non-struct value is passed.
var ErrNotStruct = errors.New("structify: value is not a struct")
// ErrFieldNotFound is returned when a field name is not found.
var ErrFieldNotFound = errors.New("structify: field not found")
// Struct wraps a struct value and provides methods for inspection and conversion.
type Struct struct {
raw any
value reflect.Value
config *config
}
// New creates a new Struct wrapper. The input must be a struct or a pointer to a struct.
// Panics if s is not a struct or pointer to struct.
func New(s any, opts ...Option) *Struct {
cfg := defaultConfig()
for _, opt := range opts {
opt(cfg)
}
v := strctVal(s)
return &Struct{
raw: s,
value: v,
config: cfg,
}
}
// Map converts the struct to a map[string]any.
// Map keys are determined by field names or the configured struct tag.
// Nested structs are recursively converted to maps unless the omitnested tag option is set.
// Fields tagged with "-" are skipped. Fields with omitempty are skipped if zero-valued.
func (s *Struct) Map() map[string]any {
return structToMap(s.value, s.config.tagName)
}
// Values returns the field values as a slice.
func (s *Struct) Values() []any {
return structValues(s.value, s.config.tagName)
}
// Names returns the exported field names.
func (s *Struct) Names() []string {
return structNames(s.value, s.config.tagName)
}
// Fields returns all non-skipped fields.
func (s *Struct) Fields() []*Field {
return getFields(s.value, s.config.tagName)
}
// Field returns a single field by name.
// Returns ErrFieldNotFound if the field does not exist.
func (s *Struct) Field(name string) (*Field, error) {
t := s.value.Type()
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
if sf.Name == name {
fv := s.value.Field(i)
opts := parseTag(sf.Tag.Get(s.config.tagName))
return &Field{
value: fv,
field: sf,
tagName: s.config.tagName,
tagOpts: opts,
}, nil
}
}
return nil, fmt.Errorf("%w: %s", ErrFieldNotFound, name)
}
// IsZero returns true if all exported fields are zero-valued.
func (s *Struct) IsZero() bool {
return isZero(s.value, s.config.tagName)
}
// HasZero returns true if any exported field is zero-valued.
func (s *Struct) HasZero() bool {
return hasZero(s.value, s.config.tagName)
}
// Name returns the struct type name.
func (s *Struct) Name() string {
return s.value.Type().Name()
}
// strctVal resolves a struct value from a value or pointer.
// Panics if s is not a struct or pointer to struct.
func strctVal(s any) reflect.Value {
v := reflect.ValueOf(s)
for v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
panic("structify: not a struct")
}
return v
}