mirror of https://github.com/databricks/cli.git
Populate struct field with `config.Value` instance if possible (#1010)
## Changes If a struct has a field of type `config.Value`, then we set it to the source value while converting a `config.Value` instance to a struct as part of a call to `convert.ToTyped`. This is convenient when dealing with deeply nested structs where functions on inner structs need access to the metadata provided by their corresponding `config.Value` (e.g. where they were defined). ## Tests Added unit tests pass.
This commit is contained in:
parent
ef97e249ec
commit
f5f57b6bf9
|
@ -4,6 +4,8 @@ import (
|
|||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/databricks/cli/libs/config"
|
||||
)
|
||||
|
||||
// structInfo holds the type information we need to efficiently
|
||||
|
@ -11,6 +13,10 @@ import (
|
|||
type structInfo struct {
|
||||
// Fields maps the JSON-name of the field to the field's index for use with [FieldByIndex].
|
||||
Fields map[string][]int
|
||||
|
||||
// ValueField maps to the field with a [config.Value].
|
||||
// The underlying type is expected to only have one of these.
|
||||
ValueField []int
|
||||
}
|
||||
|
||||
// structInfoCache caches type information.
|
||||
|
@ -68,6 +74,15 @@ func buildStructInfo(typ reflect.Type) structInfo {
|
|||
continue
|
||||
}
|
||||
|
||||
// If this field has type [config.Value], we populate it with the source [config.Value] from [ToTyped].
|
||||
if sf.IsExported() && sf.Type == configValueType {
|
||||
if out.ValueField != nil {
|
||||
panic("multiple config.Value fields")
|
||||
}
|
||||
out.ValueField = append(prefix, sf.Index...)
|
||||
continue
|
||||
}
|
||||
|
||||
name, _, _ := strings.Cut(sf.Tag.Get("json"), ",")
|
||||
if name == "" || name == "-" {
|
||||
continue
|
||||
|
@ -113,3 +128,6 @@ func (s *structInfo) FieldValues(v reflect.Value) map[string]reflect.Value {
|
|||
|
||||
return out
|
||||
}
|
||||
|
||||
// Type of [config.Value].
|
||||
var configValueType = reflect.TypeOf((*config.Value)(nil)).Elem()
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/databricks/cli/libs/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -194,3 +195,32 @@ func TestStructInfoFieldValuesAnonymousByPointer(t *testing.T) {
|
|||
assert.Empty(t, fv)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStructInfoValueFieldAbsent(t *testing.T) {
|
||||
type Tmp struct {
|
||||
Foo string `json:"foo"`
|
||||
}
|
||||
|
||||
si := getStructInfo(reflect.TypeOf(Tmp{}))
|
||||
assert.Nil(t, si.ValueField)
|
||||
}
|
||||
|
||||
func TestStructInfoValueFieldPresent(t *testing.T) {
|
||||
type Tmp struct {
|
||||
Foo config.Value
|
||||
}
|
||||
|
||||
si := getStructInfo(reflect.TypeOf(Tmp{}))
|
||||
assert.NotNil(t, si.ValueField)
|
||||
}
|
||||
|
||||
func TestStructInfoValueFieldMultiple(t *testing.T) {
|
||||
type Tmp struct {
|
||||
Foo config.Value
|
||||
Bar config.Value
|
||||
}
|
||||
|
||||
assert.Panics(t, func() {
|
||||
getStructInfo(reflect.TypeOf(Tmp{}))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -83,6 +83,12 @@ func toTypedStruct(dst reflect.Value, src config.Value) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Populate field(s) for [config.Value], if any.
|
||||
if info.ValueField != nil {
|
||||
vv := dst.FieldByIndex(info.ValueField)
|
||||
vv.Set(reflect.ValueOf(src))
|
||||
}
|
||||
|
||||
return nil
|
||||
case config.KindNil:
|
||||
dst.SetZero()
|
||||
|
|
|
@ -133,6 +133,24 @@ func TestToTypedStructNilOverwrite(t *testing.T) {
|
|||
assert.Equal(t, Tmp{}, out)
|
||||
}
|
||||
|
||||
func TestToTypedStructWithValueField(t *testing.T) {
|
||||
type Tmp struct {
|
||||
Foo string `json:"foo"`
|
||||
|
||||
ConfigValue config.Value
|
||||
}
|
||||
|
||||
var out Tmp
|
||||
v := config.V(map[string]config.Value{
|
||||
"foo": config.V("bar"),
|
||||
})
|
||||
|
||||
err := ToTyped(&out, v)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "bar", out.Foo)
|
||||
assert.Equal(t, v, out.ConfigValue)
|
||||
}
|
||||
|
||||
func TestToTypedMap(t *testing.T) {
|
||||
var out = map[string]string{}
|
||||
|
||||
|
|
Loading…
Reference in New Issue