Change implementation of find sub-tree to work with deep trees

* The previous implementation had an issue with nested structures deeper
  than one level. It would copy keys over instead of the expected object
  structure. In the example in this commit, UnmarshalKey("clothing") was
  returning "pants.size":"35" instead of "pants"{"size":"35"}
This commit is contained in:
Harley Laue 2018-04-23 14:27:51 -07:00
parent b7a4909ef7
commit e5459cc690
2 changed files with 51 additions and 3 deletions

View file

@ -1009,9 +1009,15 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} {
// it could also be a key prefix, search for that prefix to get the values from // it could also be a key prefix, search for that prefix to get the values from
// pflags that match it // pflags that match it
sub := make(map[string]interface{}) sub := make(map[string]interface{})
for key, val := range v.pflags { for _, key := range v.AllKeys() {
if flagDefault && strings.HasPrefix(key, lcaseKey) { if strings.HasPrefix(key, lcaseKey) {
sub[strings.TrimPrefix(key, lcaseKey+".")] = val.ValueString() value := v.Get(key)
keypath := strings.Split(lcaseKey, v.keyDelim)
path := strings.Split(key, v.keyDelim)[len(keypath)-1:]
lastKey := strings.ToLower(path[len(path)-1])
deepestMap := deepSearch(sub, path[1:len(path)-1])
// set innermost value
deepestMap[lastKey] = value
} }
} }
if len(sub) != 0 { if len(sub) != 0 {

View file

@ -875,7 +875,49 @@ func TestSubPflags(t *testing.T) {
v.BindPFlag("eyes", &pflag.Flag{Value: newStringValue("brown"), Changed: true}) v.BindPFlag("eyes", &pflag.Flag{Value: newStringValue("brown"), Changed: true})
v.BindPFlag("beard", &pflag.Flag{Value: newStringValue("yes"), Changed: true}) v.BindPFlag("beard", &pflag.Flag{Value: newStringValue("yes"), Changed: true})
type pants struct {
Size string
}
type clothing struct {
Jacket string
Trousers string
Pants pants
}
type cfg struct {
Name string
Clothing clothing
Age int
Eyes string
Beard bool
}
var c cfg
v.Unmarshal(&c)
assert.Equal(t, v.Get("name"), c.Name)
assert.Equal(t, v.Get("clothing.jacket"), c.Clothing.Jacket)
assert.Equal(t, v.Get("clothing.trousers"), c.Clothing.Trousers)
assert.Equal(t, v.Get("clothing.pants.size"), c.Clothing.Pants.Size)
assert.Equal(t, v.GetInt("age"), c.Age)
assert.Equal(t, v.Get("eyes"), c.Eyes)
assert.Equal(t, v.GetBool("beard"), c.Beard)
var cloth clothing
v.UnmarshalKey("clothing", &cloth)
assert.Equal(t, c.Clothing, cloth)
var p pants
v.UnmarshalKey("clothing.pants", &p)
assert.Equal(t, c.Clothing.Pants, p)
var size string
v.UnmarshalKey("clothing.pants.size", &size)
assert.Equal(t, c.Clothing.Pants.Size, size)
subv := v.Sub("clothing") subv := v.Sub("clothing")
assert.Equal(t, v.Get("clothing.jacket"), subv.Get("jacket"))
assert.Equal(t, v.Get("clothing.trousers"), subv.Get("trousers"))
assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size")) assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size"))
subv = v.Sub("clothing.pants") subv = v.Sub("clothing.pants")