feat(encoding)!: accept a map in the decoder interface

This interface is specific to decoding data into Viper's internal,
so it's okay to make it Viper specific.

BREAKING CHANGE: the decoder interface now accepts a map instead of an
interface

Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
This commit is contained in:
Mark Sagi-Kazar 2021-07-15 23:47:20 +02:00 committed by Márk Sági-Kazár
parent 6c1745665b
commit e54e7a53a5
7 changed files with 30 additions and 31 deletions

View file

@ -4,10 +4,10 @@ import (
"sync"
)
// Decoder decodes the contents of b into a v representation.
// Decoder decodes the contents of b into v.
// It's primarily used for decoding contents of a file into a map[string]interface{}.
type Decoder interface {
Decode(b []byte, v interface{}) error
Decode(b []byte, v map[string]interface{}) error
}
const (
@ -48,7 +48,7 @@ func (e *DecoderRegistry) RegisterDecoder(format string, enc Decoder) error {
}
// Decode calls the underlying Decoder based on the format.
func (e *DecoderRegistry) Decode(format string, b []byte, v interface{}) error {
func (e *DecoderRegistry) Decode(format string, b []byte, v map[string]interface{}) error {
e.mu.RLock()
decoder, ok := e.decoders[format]
e.mu.RUnlock()

View file

@ -1,16 +1,18 @@
package encoding
import (
"reflect"
"testing"
)
type decoder struct {
v interface{}
v map[string]interface{}
}
func (d decoder) Decode(_ []byte, v interface{}) error {
rv := v.(*string)
*rv = d.v.(string)
func (d decoder) Decode(_ []byte, v map[string]interface{}) error {
for key, value := range d.v {
v[key] = value
}
return nil
}
@ -44,7 +46,9 @@ func TestDecoderRegistry_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
registry := NewDecoderRegistry()
decoder := decoder{
v: "decoded value",
v: map[string]interface{}{
"key": "value",
},
}
err := registry.RegisterDecoder("myformat", decoder)
@ -52,24 +56,24 @@ func TestDecoderRegistry_Decode(t *testing.T) {
t.Fatal(err)
}
var v string
v := map[string]interface{}{}
err = registry.Decode("myformat", []byte("some value"), &v)
err = registry.Decode("myformat", []byte("key: value"), v)
if err != nil {
t.Fatal(err)
}
if v != "decoded value" {
t.Fatalf("expected 'decoded value', got: %#v", v)
if !reflect.DeepEqual(decoder.v, v) {
t.Fatalf("decoded value does not match the expected one\nactual: %+v\nexpected: %+v", v, decoder.v)
}
})
t.Run("DecoderNotFound", func(t *testing.T) {
registry := NewDecoderRegistry()
var v string
v := map[string]interface{}{}
err := registry.Decode("myformat", []byte("some value"), &v)
err := registry.Decode("myformat", nil, v)
if err != ErrDecoderNotFound {
t.Fatalf("expected ErrDecoderNotFound, got: %v", err)
}

View file

@ -35,6 +35,6 @@ func (Codec) Encode(v interface{}) ([]byte, error) {
return buf.Bytes(), nil
}
func (Codec) Decode(b []byte, v interface{}) error {
return hcl.Unmarshal(b, v)
func (Codec) Decode(b []byte, v map[string]interface{}) error {
return hcl.Unmarshal(b, &v)
}

View file

@ -12,6 +12,6 @@ func (Codec) Encode(v interface{}) ([]byte, error) {
return json.MarshalIndent(v, "", " ")
}
func (Codec) Decode(b []byte, v interface{}) error {
return json.Unmarshal(b, v)
func (Codec) Decode(b []byte, v map[string]interface{}) error {
return json.Unmarshal(b, &v)
}

View file

@ -25,21 +25,16 @@ func (Codec) Encode(v interface{}) ([]byte, error) {
return toml.Marshal(v)
}
func (Codec) Decode(b []byte, v interface{}) error {
func (Codec) Decode(b []byte, v map[string]interface{}) error {
tree, err := toml.LoadBytes(b)
if err != nil {
return err
}
if m, ok := v.(*map[string]interface{}); ok {
vmap := *m
tmap := tree.ToMap()
for k, v := range tmap {
vmap[k] = v
for key, value := range tmap {
v[key] = value
}
return nil
}
return tree.Unmarshal(v)
}

View file

@ -9,6 +9,6 @@ func (Codec) Encode(v interface{}) ([]byte, error) {
return yaml.Marshal(v)
}
func (Codec) Decode(b []byte, v interface{}) error {
return yaml.Unmarshal(b, v)
func (Codec) Decode(b []byte, v map[string]interface{}) error {
return yaml.Unmarshal(b, &v)
}

View file

@ -1635,7 +1635,7 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
switch format := strings.ToLower(v.getConfigType()); format {
case "yaml", "yml", "json", "toml", "hcl", "tfvars":
err := decoderRegistry.Decode(format, buf.Bytes(), &c)
err := decoderRegistry.Decode(format, buf.Bytes(), c)
if err != nil {
return ConfigParseError{err}
}