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>
Mark Sagi-Kazar authored 2 years ago
Márk Sági-Kazár committed 2 years ago
3 | 3 | "sync" |
4 | 4 | ) |
5 | 5 | |
6 | // Decoder decodes the contents of b into a v representation. | |
6 | // Decoder decodes the contents of b into v. | |
7 | 7 | // It's primarily used for decoding contents of a file into a map[string]interface{}. |
8 | 8 | type Decoder interface { |
9 | Decode(b []byte, v interface{}) error | |
9 | Decode(b []byte, v map[string]interface{}) error | |
10 | 10 | } |
11 | 11 | |
12 | 12 | const ( |
47 | 47 | } |
48 | 48 | |
49 | 49 | // Decode calls the underlying Decoder based on the format. |
50 | func (e *DecoderRegistry) Decode(format string, b []byte, v interface{}) error { | |
50 | func (e *DecoderRegistry) Decode(format string, b []byte, v map[string]interface{}) error { | |
51 | 51 | e.mu.RLock() |
52 | 52 | decoder, ok := e.decoders[format] |
53 | 53 | e.mu.RUnlock() |
0 | 0 | package encoding |
1 | 1 | |
2 | 2 | import ( |
3 | "reflect" | |
3 | 4 | "testing" |
4 | 5 | ) |
5 | 6 | |
6 | 7 | type decoder struct { |
7 | v interface{} | |
8 | v map[string]interface{} | |
8 | 9 | } |
9 | 10 | |
10 | func (d decoder) Decode(_ []byte, v interface{}) error { | |
11 | rv := v.(*string) | |
12 | *rv = d.v.(string) | |
11 | func (d decoder) Decode(_ []byte, v map[string]interface{}) error { | |
12 | for key, value := range d.v { | |
13 | v[key] = value | |
14 | } | |
13 | 15 | |
14 | 16 | return nil |
15 | 17 | } |
43 | 45 | t.Run("OK", func(t *testing.T) { |
44 | 46 | registry := NewDecoderRegistry() |
45 | 47 | decoder := decoder{ |
46 | v: "decoded value", | |
48 | v: map[string]interface{}{ | |
49 | "key": "value", | |
50 | }, | |
47 | 51 | } |
48 | 52 | |
49 | 53 | err := registry.RegisterDecoder("myformat", decoder) |
51 | 55 | t.Fatal(err) |
52 | 56 | } |
53 | 57 | |
54 | var v string | |
58 | v := map[string]interface{}{} | |
55 | 59 | |
56 | err = registry.Decode("myformat", []byte("some value"), &v) | |
60 | err = registry.Decode("myformat", []byte("key: value"), v) | |
57 | 61 | if err != nil { |
58 | 62 | t.Fatal(err) |
59 | 63 | } |
60 | 64 | |
61 | if v != "decoded value" { | |
62 | t.Fatalf("expected 'decoded value', got: %#v", v) | |
65 | if !reflect.DeepEqual(decoder.v, v) { | |
66 | t.Fatalf("decoded value does not match the expected one\nactual: %+v\nexpected: %+v", v, decoder.v) | |
63 | 67 | } |
64 | 68 | }) |
65 | 69 | |
66 | 70 | t.Run("DecoderNotFound", func(t *testing.T) { |
67 | 71 | registry := NewDecoderRegistry() |
68 | 72 | |
69 | var v string | |
73 | v := map[string]interface{}{} | |
70 | 74 | |
71 | err := registry.Decode("myformat", []byte("some value"), &v) | |
75 | err := registry.Decode("myformat", nil, v) | |
72 | 76 | if err != ErrDecoderNotFound { |
73 | 77 | t.Fatalf("expected ErrDecoderNotFound, got: %v", err) |
74 | 78 | } |
34 | 34 | return buf.Bytes(), nil |
35 | 35 | } |
36 | 36 | |
37 | func (Codec) Decode(b []byte, v interface{}) error { | |
38 | return hcl.Unmarshal(b, v) | |
37 | func (Codec) Decode(b []byte, v map[string]interface{}) error { | |
38 | return hcl.Unmarshal(b, &v) | |
39 | 39 | } |
11 | 11 | return json.MarshalIndent(v, "", " ") |
12 | 12 | } |
13 | 13 | |
14 | func (Codec) Decode(b []byte, v interface{}) error { | |
15 | return json.Unmarshal(b, v) | |
14 | func (Codec) Decode(b []byte, v map[string]interface{}) error { | |
15 | return json.Unmarshal(b, &v) | |
16 | 16 | } |
24 | 24 | return toml.Marshal(v) |
25 | 25 | } |
26 | 26 | |
27 | func (Codec) Decode(b []byte, v interface{}) error { | |
27 | func (Codec) Decode(b []byte, v map[string]interface{}) error { | |
28 | 28 | tree, err := toml.LoadBytes(b) |
29 | 29 | if err != nil { |
30 | 30 | return err |
31 | 31 | } |
32 | 32 | |
33 | if m, ok := v.(*map[string]interface{}); ok { | |
34 | vmap := *m | |
35 | tmap := tree.ToMap() | |
36 | for k, v := range tmap { | |
37 | vmap[k] = v | |
38 | } | |
39 | ||
40 | return nil | |
33 | tmap := tree.ToMap() | |
34 | for key, value := range tmap { | |
35 | v[key] = value | |
41 | 36 | } |
42 | 37 | |
43 | return tree.Unmarshal(v) | |
38 | return nil | |
44 | 39 | } |
8 | 8 | return yaml.Marshal(v) |
9 | 9 | } |
10 | 10 | |
11 | func (Codec) Decode(b []byte, v interface{}) error { | |
12 | return yaml.Unmarshal(b, v) | |
11 | func (Codec) Decode(b []byte, v map[string]interface{}) error { | |
12 | return yaml.Unmarshal(b, &v) | |
13 | 13 | } |
1634 | 1634 | |
1635 | 1635 | switch format := strings.ToLower(v.getConfigType()); format { |
1636 | 1636 | case "yaml", "yml", "json", "toml", "hcl", "tfvars": |
1637 | err := decoderRegistry.Decode(format, buf.Bytes(), &c) | |
1637 | err := decoderRegistry.Decode(format, buf.Bytes(), c) | |
1638 | 1638 | if err != nil { |
1639 | 1639 | return ConfigParseError{err} |
1640 | 1640 | } |