From 4c6d781515620a8dd9087ce27c4c5b916abffec6 Mon Sep 17 00:00:00 2001 From: Matt Spaulding Date: Mon, 7 Nov 2016 16:01:35 +0000 Subject: [PATCH 1/2] Add top level key support --- viper.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/viper.go b/viper.go index 4ed2d40..d45b82c 100644 --- a/viper.go +++ b/viper.go @@ -30,6 +30,7 @@ import ( "strings" "time" + "github.com/deckarep/golang-set" "github.com/fsnotify/fsnotify" "github.com/mitchellh/mapstructure" "github.com/spf13/afero" @@ -1336,6 +1337,32 @@ func (v *Viper) AllKeys() []string { return a } +// Get the set of all top level keys. +func TopLevelKeys() []string { return v.TopLevelKeys() } +func (v *Viper) TopLevelKeys() []string { + s := mapset.NewSetFromSlice(v.getMapKeys(castMapStringToMapInterface(v.aliases))) + s = s.Union(mapset.NewSetFromSlice(v.getMapKeys(v.override))) + s = s.Union(mapset.NewSetFromSlice(v.getMapKeys(castMapFlagToMapInterface(v.pflags)))) + s = s.Union(mapset.NewSetFromSlice(v.getMapKeys(castMapStringToMapInterface(v.env)))) + s = s.Union(mapset.NewSetFromSlice(v.getMapKeys(v.config))) + s = s.Union(mapset.NewSetFromSlice(v.getMapKeys(v.defaults))) + + // convert interface{} to string + keys := []string{} + for _, k := range s.ToSlice() { + keys = append(keys, k.(string)) + } + return keys +} + +func (v *Viper) getMapKeys(m map[string]interface{}) []interface{} { + keys := []interface{}{} + for key := range m { + keys = append(keys, key) + } + return keys +} + // flattenAndMergeMap recursively flattens the given map into a map[string]bool // of key paths (used as a set, easier to manipulate than a []string): // - each path is merged into a single key string, delimited with v.keyDelim (= ".") From 6816bca321749e06fb9247872811ea6188753246 Mon Sep 17 00:00:00 2001 From: Matt Spaulding Date: Mon, 7 Nov 2016 17:06:05 +0000 Subject: [PATCH 2/2] Add test for top level keys --- viper_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/viper_test.go b/viper_test.go index 60e75a3..297de51 100644 --- a/viper_test.go +++ b/viper_test.go @@ -44,6 +44,23 @@ var yamlExampleWithExtras = []byte(`Existing: true Bogus: true `) +var yamlTopLevelKeys = []byte(` +restuarants: + types: + sushi: + name: sushi2go + rating: 10 + price: average + italian: + name: "pizza place" + rating: 7 + price: average + thai: + name: "thai garden" + rating: 9 + price: high +`) + type testUnmarshalExtra struct { Existing bool } @@ -462,6 +479,35 @@ func TestAllKeysWithEnv(t *testing.T) { assert.Equal(t, expectedKeys, keys) } +func TestTopLevelKeys(t *testing.T) { + v := New() + v.SetConfigType("yaml") + r := bytes.NewReader(yamlTopLevelKeys) + err := v.ReadConfig(r) + assert.NoError(t, err) + fmt.Printf("%#v\n", v.config) + s := v.Sub("restuarants.types") + for _, i := range s.TopLevelKeys() { + ss := s.Sub(i) + switch i { + case "sushi": + assert.Equal(t, ss.GetString("name"), "sushi2go") + assert.Equal(t, ss.GetString("price"), "average") + assert.Equal(t, ss.GetInt("rating"), 10) + case "italian": + assert.Equal(t, ss.GetString("name"), "pizza place") + assert.Equal(t, ss.GetString("price"), "average") + assert.Equal(t, ss.GetInt("rating"), 7) + case "thai": + assert.Equal(t, ss.GetString("name"), "thai garden") + assert.Equal(t, ss.GetString("price"), "high") + assert.Equal(t, ss.GetInt("rating"), 9) + default: + t.Fatalf("unexpected key, %q", i) + } + } +} + func TestAliasesOfAliases(t *testing.T) { Set("Title", "Checking Case") RegisterAlias("Foo", "Bar")