diff --git a/go.mod b/go.mod index 8806a7a..3ba7b06 100644 --- a/go.mod +++ b/go.mod @@ -11,26 +11,26 @@ require ( github.com/go-viper/encoding/toml v0.1.0 github.com/go-viper/encoding/yaml v0.1.0 github.com/go-viper/mapstructure/v2 v2.0.0 - github.com/hashicorp/hcl v1.0.0 - github.com/magiconair/properties v1.8.7 - github.com/pelletier/go-toml/v2 v2.2.2 github.com/sagikazarmark/locafero v0.6.0 github.com/spf13/afero v1.11.0 github.com/spf13/cast v1.6.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 - github.com/subosito/gotenv v1.6.0 - gopkg.in/ini.v1 v1.67.0 - gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/google/go-cmp v0.6.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/internal/encoding/dotenv/codec.go b/internal/encoding/dotenv/codec.go deleted file mode 100644 index 3ebc76f..0000000 --- a/internal/encoding/dotenv/codec.go +++ /dev/null @@ -1,61 +0,0 @@ -package dotenv - -import ( - "bytes" - "fmt" - "sort" - "strings" - - "github.com/subosito/gotenv" -) - -const keyDelimiter = "_" - -// Codec implements the encoding.Encoder and encoding.Decoder interfaces for encoding data containing environment variables -// (commonly called as dotenv format). -type Codec struct{} - -func (Codec) Encode(v map[string]any) ([]byte, error) { - flattened := map[string]any{} - - flattened = flattenAndMergeMap(flattened, v, "", keyDelimiter) - - keys := make([]string, 0, len(flattened)) - - for key := range flattened { - keys = append(keys, key) - } - - sort.Strings(keys) - - var buf bytes.Buffer - - for _, key := range keys { - _, err := buf.WriteString(fmt.Sprintf("%v=%v\n", strings.ToUpper(key), flattened[key])) - if err != nil { - return nil, err - } - } - - return buf.Bytes(), nil -} - -func (Codec) Decode(b []byte, v map[string]any) error { - var buf bytes.Buffer - - _, err := buf.Write(b) - if err != nil { - return err - } - - env, err := gotenv.StrictParse(&buf) - if err != nil { - return err - } - - for key, value := range env { - v[key] = value - } - - return nil -} diff --git a/internal/encoding/dotenv/codec_test.go b/internal/encoding/dotenv/codec_test.go deleted file mode 100644 index ac2257b..0000000 --- a/internal/encoding/dotenv/codec_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package dotenv - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// original form of the data. -const original = `# key-value pair -KEY=value -` - -// encoded form of the data. -const encoded = `KEY=value -` - -// data is Viper's internal representation. -var data = map[string]any{ - "KEY": "value", -} - -func TestCodec_Encode(t *testing.T) { - codec := Codec{} - - b, err := codec.Encode(data) - require.NoError(t, err) - - assert.Equal(t, encoded, string(b)) -} - -func TestCodec_Decode(t *testing.T) { - t.Run("OK", func(t *testing.T) { - codec := Codec{} - - v := map[string]any{} - - err := codec.Decode([]byte(original), v) - require.NoError(t, err) - - assert.Equal(t, data, v) - }) - - t.Run("InvalidData", func(t *testing.T) { - codec := Codec{} - - v := map[string]any{} - - err := codec.Decode([]byte(`invalid data`), v) - require.Error(t, err) - - t.Logf("decoding failed as expected: %s", err) - }) -} diff --git a/internal/encoding/dotenv/map_utils.go b/internal/encoding/dotenv/map_utils.go deleted file mode 100644 index 8bfe0a9..0000000 --- a/internal/encoding/dotenv/map_utils.go +++ /dev/null @@ -1,41 +0,0 @@ -package dotenv - -import ( - "strings" - - "github.com/spf13/cast" -) - -// flattenAndMergeMap recursively flattens the given map into a new map -// Code is based on the function with the same name in the main package. -// TODO: move it to a common place. -func flattenAndMergeMap(shadow, m map[string]any, prefix, delimiter string) map[string]any { - if shadow != nil && prefix != "" && shadow[prefix] != nil { - // prefix is shadowed => nothing more to flatten - return shadow - } - if shadow == nil { - shadow = make(map[string]any) - } - - var m2 map[string]any - if prefix != "" { - prefix += delimiter - } - for k, val := range m { - fullKey := prefix + k - switch val := val.(type) { - case map[string]any: - m2 = val - case map[any]any: - m2 = cast.ToStringMap(val) - default: - // immediate value - shadow[strings.ToLower(fullKey)] = val - continue - } - // recursively merge to shadow map - shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter) - } - return shadow -} diff --git a/internal/encoding/hcl/codec.go b/internal/encoding/hcl/codec.go deleted file mode 100644 index d7fa8a1..0000000 --- a/internal/encoding/hcl/codec.go +++ /dev/null @@ -1,40 +0,0 @@ -package hcl - -import ( - "bytes" - "encoding/json" - - "github.com/hashicorp/hcl" - "github.com/hashicorp/hcl/hcl/printer" -) - -// Codec implements the encoding.Encoder and encoding.Decoder interfaces for HCL encoding. -// TODO: add printer config to the codec? -type Codec struct{} - -func (Codec) Encode(v map[string]any) ([]byte, error) { - b, err := json.Marshal(v) - if err != nil { - return nil, err - } - - // TODO: use printer.Format? Is the trailing newline an issue? - - ast, err := hcl.Parse(string(b)) - if err != nil { - return nil, err - } - - var buf bytes.Buffer - - err = printer.Fprint(&buf, ast.Node) - if err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -func (Codec) Decode(b []byte, v map[string]any) error { - return hcl.Unmarshal(b, &v) -} diff --git a/internal/encoding/hcl/codec_test.go b/internal/encoding/hcl/codec_test.go deleted file mode 100644 index b1d2d5a..0000000 --- a/internal/encoding/hcl/codec_test.go +++ /dev/null @@ -1,132 +0,0 @@ -package hcl - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// original form of the data. -const original = `# key-value pair -"key" = "value" - -// list -"list" = ["item1", "item2", "item3"] - -/* map */ -"map" = { - "key" = "value" -} - -/* -nested map -*/ -"nested_map" "map" { - "key" = "value" - - "list" = ["item1", "item2", "item3"] -}` - -// encoded form of the data. -const encoded = `"key" = "value" - -"list" = ["item1", "item2", "item3"] - -"map" = { - "key" = "value" -} - -"nested_map" "map" { - "key" = "value" - - "list" = ["item1", "item2", "item3"] -}` - -// decoded form of the data. -// -// In case of HCL it's slightly different from Viper's internal representation -// (e.g. map is decoded into a list of maps). -var decoded = map[string]any{ - "key": "value", - "list": []any{ - "item1", - "item2", - "item3", - }, - "map": []map[string]any{ - { - "key": "value", - }, - }, - "nested_map": []map[string]any{ - { - "map": []map[string]any{ - { - "key": "value", - "list": []any{ - "item1", - "item2", - "item3", - }, - }, - }, - }, - }, -} - -// data is Viper's internal representation. -var data = map[string]any{ - "key": "value", - "list": []any{ - "item1", - "item2", - "item3", - }, - "map": map[string]any{ - "key": "value", - }, - "nested_map": map[string]any{ - "map": map[string]any{ - "key": "value", - "list": []any{ - "item1", - "item2", - "item3", - }, - }, - }, -} - -func TestCodec_Encode(t *testing.T) { - codec := Codec{} - - b, err := codec.Encode(data) - require.NoError(t, err) - - assert.Equal(t, encoded, string(b)) -} - -func TestCodec_Decode(t *testing.T) { - t.Run("OK", func(t *testing.T) { - codec := Codec{} - - v := map[string]any{} - - err := codec.Decode([]byte(original), v) - require.NoError(t, err) - - assert.Equal(t, decoded, v) - }) - - t.Run("InvalidData", func(t *testing.T) { - codec := Codec{} - - v := map[string]any{} - - err := codec.Decode([]byte(`invalid data`), v) - require.Error(t, err) - - t.Logf("decoding failed as expected: %s", err) - }) -} diff --git a/internal/encoding/ini/codec.go b/internal/encoding/ini/codec.go deleted file mode 100644 index d91cf59..0000000 --- a/internal/encoding/ini/codec.go +++ /dev/null @@ -1,99 +0,0 @@ -package ini - -import ( - "bytes" - "sort" - "strings" - - "github.com/spf13/cast" - "gopkg.in/ini.v1" -) - -// LoadOptions contains all customized options used for load data source(s). -// This type is added here for convenience: this way consumers can import a single package called "ini". -type LoadOptions = ini.LoadOptions - -// Codec implements the encoding.Encoder and encoding.Decoder interfaces for INI encoding. -type Codec struct { - KeyDelimiter string - LoadOptions LoadOptions -} - -func (c Codec) Encode(v map[string]any) ([]byte, error) { - cfg := ini.Empty() - ini.PrettyFormat = false - - flattened := map[string]any{} - - flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter()) - - keys := make([]string, 0, len(flattened)) - - for key := range flattened { - keys = append(keys, key) - } - - sort.Strings(keys) - - for _, key := range keys { - sectionName, keyName := "", key - - lastSep := strings.LastIndex(key, ".") - if lastSep != -1 { - sectionName = key[:(lastSep)] - keyName = key[(lastSep + 1):] - } - - // TODO: is this a good idea? - if sectionName == "default" { - sectionName = "" - } - - cfg.Section(sectionName).Key(keyName).SetValue(cast.ToString(flattened[key])) - } - - var buf bytes.Buffer - - _, err := cfg.WriteTo(&buf) - if err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -func (c Codec) Decode(b []byte, v map[string]any) error { - cfg := ini.Empty(c.LoadOptions) - - err := cfg.Append(b) - if err != nil { - return err - } - - sections := cfg.Sections() - - for i := 0; i < len(sections); i++ { - section := sections[i] - keys := section.Keys() - - for j := 0; j < len(keys); j++ { - key := keys[j] - value := cfg.Section(section.Name()).Key(key.Name()).String() - - deepestMap := deepSearch(v, strings.Split(section.Name(), c.keyDelimiter())) - - // set innermost value - deepestMap[key.Name()] = value - } - } - - return nil -} - -func (c Codec) keyDelimiter() string { - if c.KeyDelimiter == "" { - return "." - } - - return c.KeyDelimiter -} diff --git a/internal/encoding/ini/codec_test.go b/internal/encoding/ini/codec_test.go deleted file mode 100644 index f9d9e70..0000000 --- a/internal/encoding/ini/codec_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package ini - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// original form of the data. -const original = `; key-value pair -key=value ; key-value pair - -# map -[map] # map -key=%(key)s - -` - -// encoded form of the data. -const encoded = `key=value - -[map] -key=value -` - -// decoded form of the data. -// -// In case of INI it's slightly different from Viper's internal representation -// (e.g. top level keys land in a section called default). -var decoded = map[string]any{ - "DEFAULT": map[string]any{ - "key": "value", - }, - "map": map[string]any{ - "key": "value", - }, -} - -// data is Viper's internal representation. -var data = map[string]any{ - "key": "value", - "map": map[string]any{ - "key": "value", - }, -} - -func TestCodec_Encode(t *testing.T) { - t.Run("OK", func(t *testing.T) { - codec := Codec{} - - b, err := codec.Encode(data) - require.NoError(t, err) - - assert.Equal(t, encoded, string(b)) - }) - - t.Run("Default", func(t *testing.T) { - codec := Codec{} - - data := map[string]any{ - "default": map[string]any{ - "key": "value", - }, - "map": map[string]any{ - "key": "value", - }, - } - - b, err := codec.Encode(data) - require.NoError(t, err) - - assert.Equal(t, encoded, string(b)) - }) -} - -func TestCodec_Decode(t *testing.T) { - t.Run("OK", func(t *testing.T) { - codec := Codec{} - - v := map[string]any{} - - err := codec.Decode([]byte(original), v) - require.NoError(t, err) - - assert.Equal(t, decoded, v) - }) - - t.Run("InvalidData", func(t *testing.T) { - codec := Codec{} - - v := map[string]any{} - - err := codec.Decode([]byte(`invalid data`), v) - require.Error(t, err) - - t.Logf("decoding failed as expected: %s", err) - }) -} diff --git a/internal/encoding/ini/map_utils.go b/internal/encoding/ini/map_utils.go deleted file mode 100644 index 490ab59..0000000 --- a/internal/encoding/ini/map_utils.go +++ /dev/null @@ -1,74 +0,0 @@ -package ini - -import ( - "strings" - - "github.com/spf13/cast" -) - -// THIS CODE IS COPIED HERE: IT SHOULD NOT BE MODIFIED -// AT SOME POINT IT WILL BE MOVED TO A COMMON PLACE -// deepSearch scans deep maps, following the key indexes listed in the -// sequence "path". -// The last value is expected to be another map, and is returned. -// -// In case intermediate keys do not exist, or map to a non-map value, -// a new map is created and inserted, and the search continues from there: -// the initial map "m" may be modified! -func deepSearch(m map[string]any, path []string) map[string]any { - for _, k := range path { - m2, ok := m[k] - if !ok { - // intermediate key does not exist - // => create it and continue from there - m3 := make(map[string]any) - m[k] = m3 - m = m3 - continue - } - m3, ok := m2.(map[string]any) - if !ok { - // intermediate key is a value - // => replace with a new map - m3 = make(map[string]any) - m[k] = m3 - } - // continue search from here - m = m3 - } - return m -} - -// flattenAndMergeMap recursively flattens the given map into a new map -// Code is based on the function with the same name in the main package. -// TODO: move it to a common place. -func flattenAndMergeMap(shadow, m map[string]any, prefix, delimiter string) map[string]any { - if shadow != nil && prefix != "" && shadow[prefix] != nil { - // prefix is shadowed => nothing more to flatten - return shadow - } - if shadow == nil { - shadow = make(map[string]any) - } - - var m2 map[string]any - if prefix != "" { - prefix += delimiter - } - for k, val := range m { - fullKey := prefix + k - switch val := val.(type) { - case map[string]any: - m2 = val - case map[any]any: - m2 = cast.ToStringMap(val) - default: - // immediate value - shadow[strings.ToLower(fullKey)] = val - continue - } - // recursively merge to shadow map - shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter) - } - return shadow -} diff --git a/internal/encoding/javaproperties/codec.go b/internal/encoding/javaproperties/codec.go deleted file mode 100644 index e92e517..0000000 --- a/internal/encoding/javaproperties/codec.go +++ /dev/null @@ -1,86 +0,0 @@ -package javaproperties - -import ( - "bytes" - "sort" - "strings" - - "github.com/magiconair/properties" - "github.com/spf13/cast" -) - -// Codec implements the encoding.Encoder and encoding.Decoder interfaces for Java properties encoding. -type Codec struct { - KeyDelimiter string - - // Store read properties on the object so that we can write back in order with comments. - // This will only be used if the configuration read is a properties file. - // TODO: drop this feature in v2 - // TODO: make use of the global properties object optional - Properties *properties.Properties -} - -func (c *Codec) Encode(v map[string]any) ([]byte, error) { - if c.Properties == nil { - c.Properties = properties.NewProperties() - } - - flattened := map[string]any{} - - flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter()) - - keys := make([]string, 0, len(flattened)) - - for key := range flattened { - keys = append(keys, key) - } - - sort.Strings(keys) - - for _, key := range keys { - _, _, err := c.Properties.Set(key, cast.ToString(flattened[key])) - if err != nil { - return nil, err - } - } - - var buf bytes.Buffer - - _, err := c.Properties.WriteComment(&buf, "#", properties.UTF8) - if err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -func (c *Codec) Decode(b []byte, v map[string]any) error { - var err error - c.Properties, err = properties.Load(b, properties.UTF8) - if err != nil { - return err - } - - for _, key := range c.Properties.Keys() { - // ignore existence check: we know it's there - value, _ := c.Properties.Get(key) - - // recursively build nested maps - path := strings.Split(key, c.keyDelimiter()) - lastKey := strings.ToLower(path[len(path)-1]) - deepestMap := deepSearch(v, path[0:len(path)-1]) - - // set innermost value - deepestMap[lastKey] = value - } - - return nil -} - -func (c Codec) keyDelimiter() string { - if c.KeyDelimiter == "" { - return "." - } - - return c.KeyDelimiter -} diff --git a/internal/encoding/javaproperties/codec_test.go b/internal/encoding/javaproperties/codec_test.go deleted file mode 100644 index ab5aec0..0000000 --- a/internal/encoding/javaproperties/codec_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package javaproperties - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// original form of the data. -const original = `#key-value pair -key = value -map.key = value -` - -// encoded form of the data. -const encoded = `key = value -map.key = value -` - -// data is Viper's internal representation. -var data = map[string]any{ - "key": "value", - "map": map[string]any{ - "key": "value", - }, -} - -func TestCodec_Encode(t *testing.T) { - codec := Codec{} - - b, err := codec.Encode(data) - require.NoError(t, err) - - assert.Equal(t, encoded, string(b)) -} - -func TestCodec_Decode(t *testing.T) { - t.Run("OK", func(t *testing.T) { - codec := Codec{} - - v := map[string]any{} - - err := codec.Decode([]byte(original), v) - require.NoError(t, err) - - assert.Equal(t, data, v) - }) - - t.Run("InvalidData", func(t *testing.T) { - t.Skip("TODO: needs invalid data example") - - codec := Codec{} - - v := map[string]any{} - - codec.Decode([]byte(``), v) - - assert.Empty(t, v) - }) -} - -func TestCodec_DecodeEncode(t *testing.T) { - codec := Codec{} - - v := map[string]any{} - - err := codec.Decode([]byte(original), v) - require.NoError(t, err) - - b, err := codec.Encode(data) - require.NoError(t, err) - - assert.Equal(t, original, string(b)) -} diff --git a/internal/encoding/javaproperties/map_utils.go b/internal/encoding/javaproperties/map_utils.go deleted file mode 100644 index 6e1aff2..0000000 --- a/internal/encoding/javaproperties/map_utils.go +++ /dev/null @@ -1,74 +0,0 @@ -package javaproperties - -import ( - "strings" - - "github.com/spf13/cast" -) - -// THIS CODE IS COPIED HERE: IT SHOULD NOT BE MODIFIED -// AT SOME POINT IT WILL BE MOVED TO A COMMON PLACE -// deepSearch scans deep maps, following the key indexes listed in the -// sequence "path". -// The last value is expected to be another map, and is returned. -// -// In case intermediate keys do not exist, or map to a non-map value, -// a new map is created and inserted, and the search continues from there: -// the initial map "m" may be modified! -func deepSearch(m map[string]any, path []string) map[string]any { - for _, k := range path { - m2, ok := m[k] - if !ok { - // intermediate key does not exist - // => create it and continue from there - m3 := make(map[string]any) - m[k] = m3 - m = m3 - continue - } - m3, ok := m2.(map[string]any) - if !ok { - // intermediate key is a value - // => replace with a new map - m3 = make(map[string]any) - m[k] = m3 - } - // continue search from here - m = m3 - } - return m -} - -// flattenAndMergeMap recursively flattens the given map into a new map -// Code is based on the function with the same name in the main package. -// TODO: move it to a common place. -func flattenAndMergeMap(shadow, m map[string]any, prefix, delimiter string) map[string]any { - if shadow != nil && prefix != "" && shadow[prefix] != nil { - // prefix is shadowed => nothing more to flatten - return shadow - } - if shadow == nil { - shadow = make(map[string]any) - } - - var m2 map[string]any - if prefix != "" { - prefix += delimiter - } - for k, val := range m { - fullKey := prefix + k - switch val := val.(type) { - case map[string]any: - m2 = val - case map[any]any: - m2 = cast.ToStringMap(val) - default: - // immediate value - shadow[strings.ToLower(fullKey)] = val - continue - } - // recursively merge to shadow map - shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter) - } - return shadow -} diff --git a/internal/encoding/toml/codec.go b/internal/encoding/toml/codec.go deleted file mode 100644 index c70aa8d..0000000 --- a/internal/encoding/toml/codec.go +++ /dev/null @@ -1,16 +0,0 @@ -package toml - -import ( - "github.com/pelletier/go-toml/v2" -) - -// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding. -type Codec struct{} - -func (Codec) Encode(v map[string]any) ([]byte, error) { - return toml.Marshal(v) -} - -func (Codec) Decode(b []byte, v map[string]any) error { - return toml.Unmarshal(b, &v) -} diff --git a/internal/encoding/toml/codec_test.go b/internal/encoding/toml/codec_test.go deleted file mode 100644 index 91d9e13..0000000 --- a/internal/encoding/toml/codec_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package toml - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// original form of the data. -const original = `# key-value pair -key = "value" -list = ["item1", "item2", "item3"] - -[map] -key = "value" - -# nested -# map -[nested_map] -[nested_map.map] -key = "value" -list = [ - "item1", - "item2", - "item3", -] -` - -// encoded form of the data. -const encoded = `key = 'value' -list = ['item1', 'item2', 'item3'] - -[map] -key = 'value' - -[nested_map] -[nested_map.map] -key = 'value' -list = ['item1', 'item2', 'item3'] -` - -// data is Viper's internal representation. -var data = map[string]any{ - "key": "value", - "list": []any{ - "item1", - "item2", - "item3", - }, - "map": map[string]any{ - "key": "value", - }, - "nested_map": map[string]any{ - "map": map[string]any{ - "key": "value", - "list": []any{ - "item1", - "item2", - "item3", - }, - }, - }, -} - -func TestCodec_Encode(t *testing.T) { - codec := Codec{} - - b, err := codec.Encode(data) - require.NoError(t, err) - - assert.Equal(t, encoded, string(b)) -} - -func TestCodec_Decode(t *testing.T) { - t.Run("OK", func(t *testing.T) { - codec := Codec{} - - v := map[string]any{} - - err := codec.Decode([]byte(original), v) - require.NoError(t, err) - - assert.Equal(t, data, v) - }) - - t.Run("InvalidData", func(t *testing.T) { - codec := Codec{} - - v := map[string]any{} - - err := codec.Decode([]byte(`invalid data`), v) - require.Error(t, err) - - t.Logf("decoding failed as expected: %s", err) - }) -} diff --git a/internal/encoding/yaml/codec.go b/internal/encoding/yaml/codec.go deleted file mode 100644 index 0368792..0000000 --- a/internal/encoding/yaml/codec.go +++ /dev/null @@ -1,14 +0,0 @@ -package yaml - -import "gopkg.in/yaml.v3" - -// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding. -type Codec struct{} - -func (Codec) Encode(v map[string]any) ([]byte, error) { - return yaml.Marshal(v) -} - -func (Codec) Decode(b []byte, v map[string]any) error { - return yaml.Unmarshal(b, &v) -} diff --git a/internal/encoding/yaml/codec_test.go b/internal/encoding/yaml/codec_test.go deleted file mode 100644 index 2d2d8ba..0000000 --- a/internal/encoding/yaml/codec_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package yaml - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// original form of the data. -const original = `# key-value pair -key: value -list: - - item1 - - item2 - - item3 -map: - key: value - -# nested -# map -nested_map: - map: - key: value - list: - - item1 - - item2 - - item3 -` - -// encoded form of the data. -const encoded = `key: value -list: - - item1 - - item2 - - item3 -map: - key: value -nested_map: - map: - key: value - list: - - item1 - - item2 - - item3 -` - -// decoded form of the data. -// -// In case of YAML it's slightly different from Viper's internal representation -// (e.g. map is decoded into a map with interface key). -var decoded = map[string]any{ - "key": "value", - "list": []any{ - "item1", - "item2", - "item3", - }, - "map": map[string]any{ - "key": "value", - }, - "nested_map": map[string]any{ - "map": map[string]any{ - "key": "value", - "list": []any{ - "item1", - "item2", - "item3", - }, - }, - }, -} - -// data is Viper's internal representation. -var data = map[string]any{ - "key": "value", - "list": []any{ - "item1", - "item2", - "item3", - }, - "map": map[string]any{ - "key": "value", - }, - "nested_map": map[string]any{ - "map": map[string]any{ - "key": "value", - "list": []any{ - "item1", - "item2", - "item3", - }, - }, - }, -} - -func TestCodec_Encode(t *testing.T) { - codec := Codec{} - - b, err := codec.Encode(data) - require.NoError(t, err) - - assert.Equal(t, encoded, string(b)) -} - -func TestCodec_Decode(t *testing.T) { - t.Run("OK", func(t *testing.T) { - codec := Codec{} - - v := map[string]any{} - - err := codec.Decode([]byte(original), v) - require.NoError(t, err) - - assert.Equal(t, decoded, v) - }) - - t.Run("InvalidData", func(t *testing.T) { - codec := Codec{} - - v := map[string]any{} - - err := codec.Decode([]byte(`invalid data`), v) - require.Error(t, err) - - t.Logf("decoding failed as expected: %s", err) - }) -}