Fix: Set() and SetDefault() insert nested values

Set() and SetDefault() fixed, so that they insert (or substitute) correctly
"nested values" (= values identified by a complete path, i.e. containing dots;
e.g. "foo.bar").
They scan (and create if needed) intermediate maps, so that the value reaches
the innermost map.

Examples :

* v.override = make(map[string]interface{})
  v.Set("foo.bar", 10)
  v.Set("foo.baz", 20)
  fmt.Println(v.override["foo"]["bar"]) // displays "10"
  fmt.Println(v.override["foo"]["baz"]) // displays "20"

Furthermore, pre-existing non-map value are erased:

* v.Set("foo.bar", 10)
  v.Set("foo.bar.baz", 20)
  fmt.Println(v.override["foo"]["bar"]) // displays "map["baz": 20]"

The low-level work is performed by function deepSearch(), it scans the given
map according to a given key path, creating intermediate maps if necessary.
This commit is contained in:
Benoit Masson 2016-06-02 15:52:28 +02:00
parent 64b6d290f2
commit 4190078ffd
2 changed files with 45 additions and 2 deletions

31
util.go
View file

@ -203,3 +203,34 @@ func parseSizeInBytes(sizeStr string) uint {
return safeMul(uint(size), multiplier) return safeMul(uint(size), multiplier)
} }
// deepSearch scans deep maps, following the key indexes listed in the
// sequence "path".
// The last value is expected to be another map, and is returned.
//
// In case intermediate keys do not exist, or map to a non-map value,
// a new map is created and inserted, and the search continues from there:
// the initial map "m" may be modified!
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
for _, k := range path {
m2, ok := m[k]
if !ok {
// intermediate key does not exist
// => create it and continue from there
m3 := make(map[string]interface{})
m[k] = m3
m = m3
continue
}
m3, ok := m2.(map[string]interface{})
if !ok {
// intermediate key is a value
// => replace with a new map
m3 = make(map[string]interface{})
m[k] = m3
}
// continue search from here
m = m3
}
return m
}

View file

@ -935,7 +935,13 @@ func SetDefault(key string, value interface{}) { v.SetDefault(key, value) }
func (v *Viper) SetDefault(key string, value interface{}) { func (v *Viper) SetDefault(key string, value interface{}) {
// If alias passed in, then set the proper default // If alias passed in, then set the proper default
key = v.realKey(strings.ToLower(key)) key = v.realKey(strings.ToLower(key))
v.defaults[key] = value
path := strings.Split(key, v.keyDelim)
lastKey := strings.ToLower(path[len(path)-1])
deepestMap := deepSearch(v.defaults, path[0:len(path)-1])
// set innermost value
deepestMap[lastKey] = value
} }
// Set sets the value for the key in the override regiser. // Set sets the value for the key in the override regiser.
@ -945,7 +951,13 @@ func Set(key string, value interface{}) { v.Set(key, value) }
func (v *Viper) Set(key string, value interface{}) { func (v *Viper) Set(key string, value interface{}) {
// If alias passed in, then set the proper override // If alias passed in, then set the proper override
key = v.realKey(strings.ToLower(key)) key = v.realKey(strings.ToLower(key))
v.override[key] = value
path := strings.Split(key, v.keyDelim)
lastKey := strings.ToLower(path[len(path)-1])
deepestMap := deepSearch(v.override, path[0:len(path)-1])
// set innermost value
deepestMap[lastKey] = value
} }
// ReadInConfig will discover and load the configuration file from disk // ReadInConfig will discover and load the configuration file from disk