Implemented ability to unmarshal keys containing dots to structs. Changed formatting of test objects for better git diffing and readibility. Fixed failing tests on Windows. - viper - [fork] go viper port for 9front
(HTM) git clone git@git.drkhsh.at/viper.git
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
(DIR) commit 99520c81d86ee7d0ee2ef143c8576be0c7533827
(DIR) parent bd1db6bb8c597678e6e65e9d8f364ed0ead2721b
(HTM) Author: inkychris <chris@inkyspider.co.uk>
Date: Sat, 16 Mar 2019 11:10:32 +0000
Implemented ability to unmarshal keys containing dots to structs.
Changed formatting of test objects for better git diffing and readibility.
Fixed failing tests on Windows.
Diffstat:
M viper.go | 20 +++++++++++++++++++-
M viper_test.go | 123 +++++++++++++++++++++++++++++--
2 files changed, 134 insertions(+), 9 deletions(-)
---
(DIR) diff --git a/viper.go b/viper.go
@@ -1789,6 +1789,24 @@ outer:
return shadow
}
+// Converts a fully qualified map key into a list of relative
+// map keys, allowing for keys to contain the delimiter themselves
+func keyComponents(v *Viper, key string) []string {
+ var result []string
+ components := strings.Split(key, v.keyDelim)
+ for index := 0; index < len(components); index++ {
+ potentialKey := strings.Join(components[0:index], v.keyDelim)
+ if v.Get(potentialKey) != nil {
+ result = append(result, potentialKey)
+ }
+ }
+ result = append(result, key)
+ for i := len(result) - 1; i > 0; i-- {
+ result[i] = strings.Replace(result[i], result[i-1]+v.keyDelim, "", 1)
+ }
+ return result
+}
+
// AllSettings merges all settings and returns them as a map[string]interface{}.
func AllSettings() map[string]interface{} { return v.AllSettings() }
func (v *Viper) AllSettings() map[string]interface{} {
@@ -1801,7 +1819,7 @@ func (v *Viper) AllSettings() map[string]interface{} {
// check just in case anything changes
continue
}
- path := strings.Split(k, v.keyDelim)
+ path := keyComponents(v, k)
lastKey := strings.ToLower(path[len(path)-1])
deepestMap := deepSearch(m, path[0:len(path)-1])
// set innermost value
(DIR) diff --git a/viper_test.go b/viper_test.go
@@ -13,6 +13,7 @@ import (
"os"
"os/exec"
"path"
+ "path/filepath"
"reflect"
"runtime"
"sort"
@@ -45,6 +46,10 @@ clothing:
age: 35
eyes : brown
beard: true
+emails:
+ steve@hacker.com:
+ created: 01/02/03
+ active: true
`)
var yamlExampleWithExtras = []byte(`Existing: true
@@ -207,11 +212,16 @@ func initHcl() {
func initDirs(t *testing.T) (string, string, func()) {
var (
- testDirs = []string{`a a`, `b`, `c\c`, `D_`}
+ testDirs = []string{`a a`, `b`, `C_`}
config = `improbable`
)
+ if runtime.GOOS != "windows" {
+ testDirs = append(testDirs, `d\d`)
+ }
+
root, err := ioutil.TempDir("", "")
+ require.NoError(t, err, "Failed to create temporary directory")
cleanup := true
defer func() {
@@ -224,7 +234,7 @@ func initDirs(t *testing.T) (string, string, func()) {
assert.Nil(t, err)
err = os.Chdir(root)
- assert.Nil(t, err)
+ require.Nil(t, err)
for _, dir := range testDirs {
err = os.Mkdir(dir, 0750)
@@ -415,7 +425,10 @@ func TestEmptyEnv(t *testing.T) {
BindEnv("type") // Empty environment variable
BindEnv("name") // Bound, but not set environment variable
- os.Clearenv()
+ os.Unsetenv("type")
+ os.Unsetenv("TYPE")
+ os.Unsetenv("name")
+ os.Unsetenv("NAME")
os.Setenv("TYPE", "")
@@ -431,7 +444,10 @@ func TestEmptyEnv_Allowed(t *testing.T) {
BindEnv("type") // Empty environment variable
BindEnv("name") // Bound, but not set environment variable
- os.Clearenv()
+ os.Unsetenv("type")
+ os.Unsetenv("TYPE")
+ os.Unsetenv("name")
+ os.Unsetenv("NAME")
os.Setenv("TYPE", "")
@@ -491,11 +507,98 @@ func TestSetEnvKeyReplacer(t *testing.T) {
func TestAllKeys(t *testing.T) {
initConfigs()
- ks := sort.StringSlice{"title", "newkey", "owner.organization", "owner.dob", "owner.bio", "name", "beard", "ppu", "batters.batter", "hobbies", "clothing.jacket", "clothing.trousers", "clothing.pants.size", "age", "hacker", "id", "type", "eyes", "p_id", "p_ppu", "p_batters.batter.type", "p_type", "p_name", "foos",
- "title_dotenv", "type_dotenv", "name_dotenv",
+ ks := sort.StringSlice{
+ "title",
+ "newkey",
+ "owner.organization",
+ "owner.dob",
+ "owner.bio",
+ "name",
+ "beard",
+ "ppu",
+ "batters.batter",
+ "hobbies",
+ "clothing.jacket",
+ "clothing.trousers",
+ "clothing.pants.size",
+ "age",
+ "hacker",
+ "id",
+ "type",
+ "eyes",
+ "p_id",
+ "p_ppu",
+ "p_batters.batter.type",
+ "p_type",
+ "p_name",
+ "foos",
+ "title_dotenv",
+ "type_dotenv",
+ "name_dotenv",
+ "emails.steve@hacker.com.active",
+ "emails.steve@hacker.com.created",
}
dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
- all := map[string]interface{}{"owner": map[string]interface{}{"organization": "MongoDB", "bio": "MongoDB Chief Developer Advocate & Hacker at Large", "dob": dob}, "title": "TOML Example", "ppu": 0.55, "eyes": "brown", "clothing": map[string]interface{}{"trousers": "denim", "jacket": "leather", "pants": map[string]interface{}{"size": "large"}}, "id": "0001", "batters": map[string]interface{}{"batter": []interface{}{map[string]interface{}{"type": "Regular"}, map[string]interface{}{"type": "Chocolate"}, map[string]interface{}{"type": "Blueberry"}, map[string]interface{}{"type": "Devil's Food"}}}, "hacker": true, "beard": true, "hobbies": []interface{}{"skateboarding", "snowboarding", "go"}, "age": 35, "type": "donut", "newkey": "remote", "name": "Cake", "p_id": "0001", "p_ppu": "0.55", "p_name": "Cake", "p_batters": map[string]interface{}{"batter": map[string]interface{}{"type": "Regular"}}, "p_type": "donut", "foos": []map[string]interface{}{map[string]interface{}{"foo": []map[string]interface{}{map[string]interface{}{"key": 1}, map[string]interface{}{"key": 2}, map[string]interface{}{"key": 3}, map[string]interface{}{"key": 4}}}}, "title_dotenv": "DotEnv Example", "type_dotenv": "donut", "name_dotenv": "Cake"}
+ all := map[string]interface{}{
+ "owner": map[string]interface{}{
+ "organization": "MongoDB",
+ "bio": "MongoDB Chief Developer Advocate & Hacker at Large",
+ "dob": dob,
+ },
+ "title": "TOML Example",
+ "ppu": 0.55,
+ "emails": map[string]interface{}{
+ "steve@hacker.com": map[string]interface{}{
+ "active": true,
+ "created": "01/02/03",
+ },
+ },
+ "eyes": "brown",
+ "clothing": map[string]interface{}{
+ "trousers": "denim",
+ "jacket": "leather",
+ "pants": map[string]interface{}{"size": "large"},
+ },
+ "id": "0001",
+ "batters": map[string]interface{}{
+ "batter": []interface{}{
+ map[string]interface{}{"type": "Regular"},
+ map[string]interface{}{"type": "Chocolate"},
+ map[string]interface{}{"type": "Blueberry"},
+ map[string]interface{}{"type": "Devil's Food"},
+ },
+ },
+ "hacker": true,
+ "beard": true,
+ "hobbies": []interface{}{
+ "skateboarding",
+ "snowboarding",
+ "go",
+ },
+ "age": 35,
+ "type": "donut",
+ "newkey": "remote",
+ "name": "Cake",
+ "p_id": "0001",
+ "p_ppu": "0.55",
+ "p_name": "Cake",
+ "p_batters": map[string]interface{}{
+ "batter": map[string]interface{}{"type": "Regular"},
+ },
+ "p_type": "donut",
+ "foos": []map[string]interface{}{
+ {
+ "foo": []map[string]interface{}{
+ {"key": 1},
+ {"key": 2},
+ {"key": 3},
+ {"key": 4}},
+ },
+ },
+ "title_dotenv": "DotEnv Example",
+ "type_dotenv": "donut",
+ "name_dotenv": "Cake",
+ }
allkeys := sort.StringSlice(AllKeys())
allkeys.Sort()
@@ -960,7 +1063,7 @@ func TestDirsSearch(t *testing.T) {
err = v.ReadInConfig()
assert.Nil(t, err)
- assert.Equal(t, `value is `+path.Base(v.configPaths[0]), v.GetString(`key`))
+ assert.Equal(t, `value is `+filepath.Base(v.configPaths[0]), v.GetString(`key`))
}
func TestWrongDirsSearchNotFound(t *testing.T) {
@@ -1213,6 +1316,10 @@ clothing:
pants:
size: large
trousers: denim
+emails:
+ steve@hacker.com:
+ active: true
+ created: 01/02/03
eyes: brown
hacker: true
hobbies: