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

View file

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

View file

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

View file

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

View file

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

View file

@ -9,6 +9,6 @@ func (Codec) Encode(v interface{}) ([]byte, error) {
return yaml.Marshal(v) return yaml.Marshal(v)
} }
func (Codec) Decode(b []byte, v interface{}) error { func (Codec) Decode(b []byte, v map[string]interface{}) error {
return yaml.Unmarshal(b, v) 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 { switch format := strings.ToLower(v.getConfigType()); format {
case "yaml", "yml", "json", "toml", "hcl", "tfvars": case "yaml", "yml", "json", "toml", "hcl", "tfvars":
err := decoderRegistry.Decode(format, buf.Bytes(), &c) err := decoderRegistry.Decode(format, buf.Bytes(), c)
if err != nil { if err != nil {
return ConfigParseError{err} return ConfigParseError{err}
} }