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.
This commit is contained in:
inkychris 2019-03-16 11:10:32 +00:00 committed by Márk Sági-Kazár
parent bd1db6bb8c
commit 99520c81d8
2 changed files with 134 additions and 9 deletions

View File

@ -1789,6 +1789,24 @@ outer:
return shadow 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{}. // AllSettings merges all settings and returns them as a map[string]interface{}.
func AllSettings() map[string]interface{} { return v.AllSettings() } func AllSettings() map[string]interface{} { return v.AllSettings() }
func (v *Viper) AllSettings() map[string]interface{} { func (v *Viper) AllSettings() map[string]interface{} {
@ -1801,7 +1819,7 @@ func (v *Viper) AllSettings() map[string]interface{} {
// check just in case anything changes // check just in case anything changes
continue continue
} }
path := strings.Split(k, v.keyDelim) path := keyComponents(v, k)
lastKey := strings.ToLower(path[len(path)-1]) lastKey := strings.ToLower(path[len(path)-1])
deepestMap := deepSearch(m, path[0:len(path)-1]) deepestMap := deepSearch(m, path[0:len(path)-1])
// set innermost value // set innermost value

View File

@ -13,6 +13,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path" "path"
"path/filepath"
"reflect" "reflect"
"runtime" "runtime"
"sort" "sort"
@ -45,6 +46,10 @@ clothing:
age: 35 age: 35
eyes : brown eyes : brown
beard: true beard: true
emails:
steve@hacker.com:
created: 01/02/03
active: true
`) `)
var yamlExampleWithExtras = []byte(`Existing: true var yamlExampleWithExtras = []byte(`Existing: true
@ -207,11 +212,16 @@ func initHcl() {
func initDirs(t *testing.T) (string, string, func()) { func initDirs(t *testing.T) (string, string, func()) {
var ( var (
testDirs = []string{`a a`, `b`, `c\c`, `D_`} testDirs = []string{`a a`, `b`, `C_`}
config = `improbable` config = `improbable`
) )
if runtime.GOOS != "windows" {
testDirs = append(testDirs, `d\d`)
}
root, err := ioutil.TempDir("", "") root, err := ioutil.TempDir("", "")
require.NoError(t, err, "Failed to create temporary directory")
cleanup := true cleanup := true
defer func() { defer func() {
@ -224,7 +234,7 @@ func initDirs(t *testing.T) (string, string, func()) {
assert.Nil(t, err) assert.Nil(t, err)
err = os.Chdir(root) err = os.Chdir(root)
assert.Nil(t, err) require.Nil(t, err)
for _, dir := range testDirs { for _, dir := range testDirs {
err = os.Mkdir(dir, 0750) err = os.Mkdir(dir, 0750)
@ -415,7 +425,10 @@ func TestEmptyEnv(t *testing.T) {
BindEnv("type") // Empty environment variable BindEnv("type") // Empty environment variable
BindEnv("name") // Bound, but not set 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", "") os.Setenv("TYPE", "")
@ -431,7 +444,10 @@ func TestEmptyEnv_Allowed(t *testing.T) {
BindEnv("type") // Empty environment variable BindEnv("type") // Empty environment variable
BindEnv("name") // Bound, but not set 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", "") os.Setenv("TYPE", "")
@ -491,11 +507,98 @@ func TestSetEnvKeyReplacer(t *testing.T) {
func TestAllKeys(t *testing.T) { func TestAllKeys(t *testing.T) {
initConfigs() 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", ks := sort.StringSlice{
"title_dotenv", "type_dotenv", "name_dotenv", "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") 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.StringSlice(AllKeys())
allkeys.Sort() allkeys.Sort()
@ -960,7 +1063,7 @@ func TestDirsSearch(t *testing.T) {
err = v.ReadInConfig() err = v.ReadInConfig()
assert.Nil(t, err) 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) { func TestWrongDirsSearchNotFound(t *testing.T) {
@ -1213,6 +1316,10 @@ clothing:
pants: pants:
size: large size: large
trousers: denim trousers: denim
emails:
steve@hacker.com:
active: true
created: 01/02/03
eyes: brown eyes: brown
hacker: true hacker: true
hobbies: hobbies: