Skip to content

Commit

Permalink
make dotenv.Parser behave like env.Provider (#179)
Browse files Browse the repository at this point in the history
* make dotenv.Parser behave like env.Provider

* copy env.Provider for comparison.
  • Loading branch information
jxsl13 committed Nov 1, 2022
1 parent 9d2379c commit d38d286
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 8 deletions.
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)
}
})
}
}

0 comments on commit d38d286

Please sign in to comment.