From e54e7a53a5136f962e178fdd7ce95114bd49570c Mon Sep 17 00:00:00 2001 From: Mark Sagi-Kazar Date: Thu, 15 Jul 2021 23:47:20 +0200 Subject: [PATCH] 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 --- internal/encoding/decoder.go | 6 +++--- internal/encoding/decoder_test.go | 26 +++++++++++++++----------- internal/encoding/hcl/codec.go | 4 ++-- internal/encoding/json/codec.go | 4 ++-- internal/encoding/toml/codec.go | 15 +++++---------- internal/encoding/yaml/codec.go | 4 ++-- viper.go | 2 +- 7 files changed, 30 insertions(+), 31 deletions(-) diff --git a/internal/encoding/decoder.go b/internal/encoding/decoder.go index 08b1bb6..f472e9f 100644 --- a/internal/encoding/decoder.go +++ b/internal/encoding/decoder.go @@ -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() diff --git a/internal/encoding/decoder_test.go b/internal/encoding/decoder_test.go index 80e6688..6cb56d0 100644 --- a/internal/encoding/decoder_test.go +++ b/internal/encoding/decoder_test.go @@ -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) } diff --git a/internal/encoding/hcl/codec.go b/internal/encoding/hcl/codec.go index f3e4ab1..6e2d508 100644 --- a/internal/encoding/hcl/codec.go +++ b/internal/encoding/hcl/codec.go @@ -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) } diff --git a/internal/encoding/json/codec.go b/internal/encoding/json/codec.go index dff9ec9..f8fbdd0 100644 --- a/internal/encoding/json/codec.go +++ b/internal/encoding/json/codec.go @@ -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) } diff --git a/internal/encoding/toml/codec.go b/internal/encoding/toml/codec.go index c043802..48b0562 100644 --- a/internal/encoding/toml/codec.go +++ b/internal/encoding/toml/codec.go @@ -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 - } - - return nil + tmap := tree.ToMap() + for key, value := range tmap { + v[key] = value } - return tree.Unmarshal(v) + return nil } diff --git a/internal/encoding/yaml/codec.go b/internal/encoding/yaml/codec.go index f94b269..a46a882 100644 --- a/internal/encoding/yaml/codec.go +++ b/internal/encoding/yaml/codec.go @@ -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) } diff --git a/viper.go b/viper.go index 980ab5e..38e29b5 100644 --- a/viper.go +++ b/viper.go @@ -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} }