/
visiblefields.go
93 lines (79 loc) · 2.42 KB
/
visiblefields.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
// Copyright 2022 Franklin "Snaipe" Mathieu.
//
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
package reflectutil
import (
"reflect"
"snai.pe/boa/encoding"
)
type StructField struct {
reflect.StructField
Value reflect.Value
Options FieldOpts
}
func VisibleFields(val reflect.Value, convention encoding.NamingConvention, unmarshaler interface{}) ([]StructField, map[string]StructField) {
t := val.Type()
fields := make(map[string]StructField, t.NumField()*2)
order := make([]StructField, 0, t.NumField())
order = walkFields(fields, order, nil, val, val.Type(), convention, unmarshaler)
// Remove fields with nil indices -- they've been shadowed.
l := 0
for _, field := range order {
if field.Index == nil {
continue
}
order[l] = field
l++
}
return order[:l], fields
}
func walkFields(fields map[string]StructField, order []StructField, index []int, val reflect.Value, typ reflect.Type, convention encoding.NamingConvention, unmarshaler interface{}) []StructField {
for i := 0; i < typ.NumField(); i++ {
var (
field = typ.Field(i)
elem = val.Field(i)
idx = append(index, i)
)
// Ignore private fields
if field.PkgPath != "" {
continue
}
opts := ParseFieldOpts(field.Tag, unmarshaler, convention)
if opts.Ignore {
continue
}
if field.Anonymous && opts.Name == "" || opts.Inline {
order = walkFields(fields, order, idx, elem, field.Type, opts.Naming, unmarshaler)
continue
}
if opts.Name == "" && opts.Naming != nil {
opts.Name = opts.Naming.Format(field.Name)
}
if existing, ok := fields[opts.Name]; ok {
switch {
case len(existing.Index) == len(idx):
existing.Index = nil
continue
case len(existing.Index) > len(idx):
existing.Index = nil
default:
continue
}
}
// Override the field Index so that v.FieldByIndex() works with the top-level value.
field.Index = append([]int(nil), idx...)
structfield := StructField{field, elem, opts}
fields[opts.Name] = structfield
order = append(order, structfield)
}
return order
}
func VisibleFieldsAsMapEntries(val reflect.Value, convention encoding.NamingConvention, marshaler interface{}) []MapEntry {
fields, _ := VisibleFields(val, convention, marshaler)
entries := make([]MapEntry, len(fields))
for i, field := range fields {
entries[i] = MapEntry{Key: field.Options.Name, Value: field.Value, Options: field.Options}
}
return entries
}