Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make dotenv.Parser behave like env.Provider #179

Merged
merged 2 commits into from Nov 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
54 changes: 48 additions & 6 deletions parsers/dotenv/dotenv.go
Expand Up @@ -3,18 +3,38 @@ package dotenv

import (
"fmt"
"strings"

"github.com/joho/godotenv"
"github.com/knadh/koanf/maps"
)

// DotEnv implements a DotEnv parser.
type DotEnv struct{}
// DotEnv implements a DOTENV parser.
type DotEnv struct {
delim string
prefix string

cb func(key string, value string) (string, interface{})
reverseCB map[string]string
}

// Parser returns a DOTENV Parser.
func Parser() *DotEnv {
return &DotEnv{}
}

// ParserEnv allows to make the DOTENV Parser behave like the env.Provider.
func ParserEnv(prefix, delim string, cb func(s string) string) *DotEnv {
return &DotEnv{
delim: delim,
prefix: prefix,
cb: func(key, value string) (string, interface{}) {
return cb(key), value
},
reverseCB: make(map[string]string),
}
}

// Unmarshal parses the given DOTENV bytes.
func (p *DotEnv) Unmarshal(b []byte) (map[string]interface{}, error) {
// Unmarshal DOTENV from []byte
Expand All @@ -25,19 +45,41 @@ func (p *DotEnv) Unmarshal(b []byte) (map[string]interface{}, error) {

// Convert a map[string]string to a map[string]interface{}
mp := make(map[string]interface{})
for k, v := range r {
mp[k] = v
for sourceKey, v := range r {
if !strings.HasPrefix(sourceKey, p.prefix) {
continue
}

if p.cb != nil {
targetKey, value := p.cb(sourceKey, v)
p.reverseCB[targetKey] = sourceKey
mp[targetKey] = value
} else {
mp[sourceKey] = v
}

}

if p.delim != "" {
mp = maps.Unflatten(mp, p.delim)
}
return mp, nil
}

// Marshal marshals the given config map to DOTENV bytes.
func (p *DotEnv) Marshal(o map[string]interface{}) ([]byte, error) {
if p.delim != "" {
o, _ = maps.Flatten(o, nil, p.delim)
}

// Convert a map[string]interface{} to a map[string]string
mp := make(map[string]string)
for k, v := range o {
mp[k] = fmt.Sprint(v)
for targetKey, v := range o {
if sourceKey, found := p.reverseCB[targetKey]; found {
targetKey = sourceKey
}

mp[targetKey] = fmt.Sprint(v)
}

// Unmarshal to string
Expand Down
84 changes: 82 additions & 2 deletions parsers/dotenv/dotenv_test.go
@@ -1,8 +1,10 @@
package dotenv

import (
"github.com/stretchr/testify/assert"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func TestDotEnv_Marshal(t *testing.T) {
Expand Down Expand Up @@ -65,7 +67,7 @@ func TestDotEnv_Marshal(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
out, err := de.Marshal(tc.cfg)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.expOut, out)
assert.Equal(t, string(tc.expOut), string(out))
})
}
}
Expand Down Expand Up @@ -138,3 +140,81 @@ func TestDotEnv_Unmarshal(t *testing.T) {
})
}
}

func TestCompareToEnvProvider(t *testing.T) {

testCases := []struct {
name string
prefix string
delim string
key string
value string
expKey string
expValue string
cb func(key string) string
want *DotEnv
}{
{
name: "Nil cb",
prefix: "TESTVAR_",
delim: ".",
want: &DotEnv{
prefix: "TESTVAR_",
delim: ".",
},
},
{
name: "Simple cb",
prefix: "TESTVAR_",
delim: ".",
key: "TestKey",
value: "TestVal",
expKey: "testkey",
expValue: "TestVal",
cb: func(key string) string {
return strings.ToLower(key)
},
want: &DotEnv{
prefix: "TESTVAR_",
delim: ".",
},
},
{
name: "Empty string nil cb",
prefix: "",
delim: ".",
want: &DotEnv{
prefix: "",
delim: ".",
},
},
{
name: "Cb is given",
prefix: "",
delim: ".",
key: "test_key",
value: "test_val",
expKey: "TEST.KEY",
expValue: "test_val",
cb: func(key string) string {
return strings.Replace(strings.ToUpper(key), "_", ".", -1)
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
gotProvider := ParserEnv(tc.prefix, tc.delim, tc.cb)
if tc.cb == nil {
assert.Equal(t, tc.prefix, gotProvider.prefix)
assert.Equal(t, tc.delim, gotProvider.delim)
// do not compare cb or reverseCB
}
if tc.cb != nil {
k, v := gotProvider.cb(tc.key, tc.value)
assert.Equal(t, tc.expKey, k)
assert.Equal(t, tc.expValue, v)
}
})
}
}