mirror of
https://github.com/spf13/viper
synced 2025-05-06 12:17:18 +00:00
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:
parent
64b6d290f2
commit
4190078ffd
2 changed files with 45 additions and 2 deletions
31
util.go
31
util.go
|
@ -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
|
||||||
|
}
|
||||||
|
|
16
viper.go
16
viper.go
|
@ -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
|
||||||
|
|
Loading…
Add table
Reference in a new issue