Support "final" flag for KV
This enables us to store structure as JSON under a specific (final) key
This commit is contained in:
@@ -48,7 +48,10 @@ func DecodeKV(kv KV, dst interface{}, pp ...string) (err error) {
|
||||
length := valueOf.NumField()
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
var structField = valueOf.Field(i)
|
||||
var (
|
||||
structField = valueOf.Field(i)
|
||||
tags = make(map[string]bool)
|
||||
)
|
||||
|
||||
if !structField.CanSet() {
|
||||
continue
|
||||
@@ -80,6 +83,7 @@ func DecodeKV(kv KV, dst interface{}, pp ...string) (err error) {
|
||||
|
||||
for f := 1; f < len(tagFlags); f++ {
|
||||
// @todo resolve i18n and other flags...
|
||||
tags[tagFlags[f]] = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,36 +106,38 @@ func DecodeKV(kv KV, dst interface{}, pp ...string) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Handles structs
|
||||
//
|
||||
// It calls DecodeKV recursively
|
||||
if structField.Kind() == reflect.Struct {
|
||||
if err = DecodeKV(kv.Filter(key), structValue, key); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// Handles map values
|
||||
if structField.Kind() == reflect.Map {
|
||||
if structField.IsNil() {
|
||||
// allocate new map
|
||||
structField.Set(reflect.MakeMap(structField.Type()))
|
||||
}
|
||||
|
||||
// cut KV key prefix and use the rest for the map key
|
||||
for k, val := range kv.CutPrefix(key + ".") {
|
||||
mapValue := reflect.New(structField.Type().Elem())
|
||||
err = val.Unmarshal(mapValue.Interface())
|
||||
if err != nil {
|
||||
if !tags["final"] {
|
||||
// Handles structs
|
||||
//
|
||||
// It calls DecodeKV recursively
|
||||
if structField.Kind() == reflect.Struct {
|
||||
if err = DecodeKV(kv.Filter(key), structValue, key); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
structField.SetMapIndex(reflect.ValueOf(k), mapValue.Elem())
|
||||
continue
|
||||
}
|
||||
|
||||
continue
|
||||
// Handles map values
|
||||
if structField.Kind() == reflect.Map {
|
||||
if structField.IsNil() {
|
||||
// allocate new map
|
||||
structField.Set(reflect.MakeMap(structField.Type()))
|
||||
}
|
||||
|
||||
// cut KV key prefix and use the rest for the map key
|
||||
for k, val := range kv.CutPrefix(key + ".") {
|
||||
mapValue := reflect.New(structField.Type().Elem())
|
||||
err = val.Unmarshal(mapValue.Interface())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
structField.SetMapIndex(reflect.ValueOf(k), mapValue.Elem())
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Native type
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx/types"
|
||||
@@ -132,3 +133,34 @@ func TestDecodeHandler(t *testing.T) {
|
||||
require.Equal(t, 1, aux.Foo.set)
|
||||
require.Equal(t, 1, aux.Bar.set)
|
||||
}
|
||||
|
||||
func TestDecodeKV_WithFinalTag(t *testing.T) {
|
||||
type (
|
||||
dst struct {
|
||||
NotFinal struct {
|
||||
Foo string
|
||||
Sub struct {
|
||||
SubFoo int
|
||||
}
|
||||
}
|
||||
|
||||
IsFinal struct {
|
||||
Foo string
|
||||
Sub struct {
|
||||
SubFoo int
|
||||
}
|
||||
} `kv:",final"`
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
aux = dst{}
|
||||
kv = KV{
|
||||
"notFinal.foo": types.JSONText(`"42"`),
|
||||
"notFinal.sub.subFoo": types.JSONText(`42`),
|
||||
"isFinal": types.JSONText(`{"Foo":"final42","Sub":{"SubFoo":42}}`),
|
||||
}
|
||||
)
|
||||
|
||||
spew.Dump(DecodeKV(kv, &aux), aux)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user