From 78f7390c39e274770c5dc5a969ac5f8886103e87 Mon Sep 17 00:00:00 2001 From: inkychris Date: Wed, 18 Sep 2019 09:07:48 +0100 Subject: [PATCH] Added function to check for partial keys existing: Includes checking PFlags and Envs, unlike IsSet() (#764) --- viper.go | 25 +++++++++++++++++++++++-- viper_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/viper.go b/viper.go index 491799c..026bec7 100644 --- a/viper.go +++ b/viper.go @@ -1789,14 +1789,35 @@ outer: return shadow } +// Checks to see if a key may exist as a parent node. +// Similar to v.IsSet(), but checks pflags as a fallback. +func (v *Viper) isComponent(key string) bool { + lcaseKey := strings.ToLower(key) + val := v.find(lcaseKey) + if val != nil { + return true + } + for k := range v.pflags { + if strings.HasPrefix(k, lcaseKey) { + return true + } + } + for k := range v.env { + if strings.HasPrefix(k, lcaseKey) { + return true + } + } + return false +} + // 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++ { + for index := 1; index < len(components); index++ { potentialKey := strings.Join(components[0:index], v.keyDelim) - if v.Get(potentialKey) != nil { + if v.isComponent(potentialKey) { result = append(result, potentialKey) } } diff --git a/viper_test.go b/viper_test.go index 3c15ed2..0774aa4 100644 --- a/viper_test.go +++ b/viper_test.go @@ -758,6 +758,39 @@ func TestBindPFlags(t *testing.T) { } +func TestUnmarshalOnlyPFlagSet(t *testing.T) { + flags := pflag.NewFlagSet("test", pflag.ContinueOnError) + flags.String("foo.bar", "cobra_flag", "") + + v := New() + assert.NoError(t, v.BindPFlags(flags)) + + config := &struct { + Foo struct { + Bar string + } + }{} + + assert.NoError(t, v.Unmarshal(config)) + assert.Equal(t, "cobra_flag", config.Foo.Bar) +} + +func TestUnmarshalOnlyEnvSet(t *testing.T) { + v := New() + v.SetEnvPrefix("viper") + assert.NoError(t, v.BindEnv("foo.bar")) + assert.NoError(t, os.Setenv("VIPER_FOO.BAR", "testval")) + + config := &struct { + Foo struct { + Bar string + } + }{} + + assert.NoError(t, v.Unmarshal(config)) + assert.Equal(t, "testval", config.Foo.Bar) +} + func TestBindPFlagsStringSlice(t *testing.T) { tests := []struct { Expected []string