Skip to content

Commit 092c8da

Browse files
authored
dyn: Modify FieldValues to return ordered fields (#3327)
The fields have natural order, existing API destroys it by returning map. This PR changes FieldValues() to return a more appropriate data structure. Needed for / extracted from #3230
1 parent 68bef23 commit 092c8da

File tree

3 files changed

+26
-12
lines changed

3 files changed

+26
-12
lines changed

libs/dyn/convert/from_typed.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@ func fromTypedStruct(src reflect.Value, ref dyn.Value, options ...fromTypedOptio
102102
refm, _ := ref.AsMap()
103103
out := dyn.NewMapping()
104104
info := getStructInfo(src.Type())
105-
for k, v := range info.FieldValues(src) {
105+
for _, fieldval := range info.FieldValues(src) {
106+
k := fieldval.Key
107+
v := fieldval.Value
106108
pair, ok := refm.GetPairByString(k)
107109
refloc := pair.Key.Locations()
108110
refv := pair.Value

libs/dyn/convert/struct_info.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import (
1111
// structInfo holds the type information we need to efficiently
1212
// convert data from a [dyn.Value] to a Go struct.
1313
type structInfo struct {
14+
// FieldNames is ordered list of fields
15+
FieldNames []string
16+
1417
// Fields maps the JSON-name of the field to the field's index for use with [FieldByIndex].
1518
Fields map[string][]int
1619

@@ -94,17 +97,24 @@ func buildStructInfo(typ reflect.Type) structInfo {
9497
continue
9598
}
9699

100+
out.FieldNames = append(out.FieldNames, name)
97101
out.Fields[name] = append(prefix, sf.Index...)
98102
}
99103
}
100104

101105
return out
102106
}
103107

104-
func (s *structInfo) FieldValues(v reflect.Value) map[string]reflect.Value {
105-
out := make(map[string]reflect.Value)
108+
type FieldValue struct {
109+
Key string
110+
Value reflect.Value
111+
}
112+
113+
func (s *structInfo) FieldValues(v reflect.Value) []FieldValue {
114+
out := make([]FieldValue, 0, len(s.Fields))
106115

107-
for k, index := range s.Fields {
116+
for _, k := range s.FieldNames {
117+
index := s.Fields[k]
108118
fv := v
109119

110120
// Locate value in struct (it could be an embedded type).
@@ -122,7 +132,7 @@ func (s *structInfo) FieldValues(v reflect.Value) map[string]reflect.Value {
122132
}
123133

124134
if fv.IsValid() {
125-
out[k] = fv
135+
out = append(out, FieldValue{Key: k, Value: fv})
126136
}
127137
}
128138

libs/dyn/convert/struct_info_test.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,10 @@ func TestStructInfoFieldValues(t *testing.T) {
103103
si := getStructInfo(reflect.TypeOf(Tmp{}))
104104
fv := si.FieldValues(reflect.ValueOf(src))
105105
assert.Len(t, fv, 2)
106-
assert.Equal(t, "foo", fv["foo"].String())
107-
assert.Equal(t, "bar", fv["bar"].String())
106+
assert.Equal(t, "foo", fv[0].Key)
107+
assert.True(t, reflect.ValueOf("foo").Equal(fv[0].Value))
108+
assert.Equal(t, "bar", fv[1].Key)
109+
assert.True(t, reflect.ValueOf("bar").Equal(fv[1].Value))
108110
}
109111

110112
func TestStructInfoFieldValuesAnonymousByValue(t *testing.T) {
@@ -133,8 +135,8 @@ func TestStructInfoFieldValuesAnonymousByValue(t *testing.T) {
133135
si := getStructInfo(reflect.TypeOf(Tmp{}))
134136
fv := si.FieldValues(reflect.ValueOf(src))
135137
assert.Len(t, fv, 2)
136-
assert.Equal(t, "foo", fv["foo"].String())
137-
assert.Equal(t, "bar", fv["bar"].String())
138+
assert.Equal(t, "foo", fv[0].Key)
139+
assert.Equal(t, "bar", fv[1].Key)
138140
}
139141

140142
func TestStructInfoFieldValuesAnonymousByPointer(t *testing.T) {
@@ -165,8 +167,8 @@ func TestStructInfoFieldValuesAnonymousByPointer(t *testing.T) {
165167
si := getStructInfo(reflect.TypeOf(Tmp{}))
166168
fv := si.FieldValues(reflect.ValueOf(src))
167169
assert.Len(t, fv, 2)
168-
assert.Equal(t, "foo", fv["foo"].String())
169-
assert.Equal(t, "bar", fv["bar"].String())
170+
assert.Equal(t, "foo", fv[0].Key)
171+
assert.Equal(t, "bar", fv[1].Key)
170172
})
171173

172174
// Test that fields of embedded types are skipped if the embedded type is nil.
@@ -181,7 +183,7 @@ func TestStructInfoFieldValuesAnonymousByPointer(t *testing.T) {
181183
si := getStructInfo(reflect.TypeOf(Tmp{}))
182184
fv := si.FieldValues(reflect.ValueOf(src))
183185
assert.Len(t, fv, 1)
184-
assert.Equal(t, "foo", fv["foo"].String())
186+
assert.Equal(t, "foo", fv[0].Key)
185187
})
186188

187189
// Test that fields of embedded types are skipped if the embedded type is nil.

0 commit comments

Comments
 (0)