From 47bb9ab45aaf5fd267c9352f61884b5db964fb64 Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Sun, 19 Apr 2020 10:15:15 +0200 Subject: [PATCH] Support "final" flag for KV This enables us to store structure as JSON under a specific (final) key --- pkg/settings/kv_decoder.go | 58 ++++++++++++++++++--------------- pkg/settings/kv_decoder_test.go | 32 ++++++++++++++++++ 2 files changed, 64 insertions(+), 26 deletions(-) diff --git a/pkg/settings/kv_decoder.go b/pkg/settings/kv_decoder.go index 7cf77d08a..a054324dc 100644 --- a/pkg/settings/kv_decoder.go +++ b/pkg/settings/kv_decoder.go @@ -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 diff --git a/pkg/settings/kv_decoder_test.go b/pkg/settings/kv_decoder_test.go index 47558fb51..96fd282b8 100644 --- a/pkg/settings/kv_decoder_test.go +++ b/pkg/settings/kv_decoder_test.go @@ -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) +}