From 6d96b16143c37dc3b735068c55154121d09921a6 Mon Sep 17 00:00:00 2001 From: Kolbe Kegel Date: Tue, 22 Aug 2017 23:35:11 -0700 Subject: [PATCH 1/2] Added UnmarshalKeyExact() as well as non-method UnmarshalExact(). --- viper.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/viper.go b/viper.go index 2a221e5..4b93427 100644 --- a/viper.go +++ b/viper.go @@ -768,6 +768,7 @@ func decode(input interface{}, config *mapstructure.DecoderConfig) error { // UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent // in the destination struct. +func UnmarshalExact(rawVal interface{}) error { return v.UnmarshalExact(rawVal) } func (v *Viper) UnmarshalExact(rawVal interface{}) error { config := defaultDecoderConfig(rawVal) config.ErrorUnused = true @@ -782,6 +783,23 @@ func (v *Viper) UnmarshalExact(rawVal interface{}) error { return nil } +// UnmarshalKeyExact takes a single key and unmarshals it into a Struct, erroring if a field +// is nonexistent in the destination struct. +func UnmarshalKeyExact(key string, rawVal interface{}) error { return v.UnmarshalKeyExact(key, rawVal) } +func (v *Viper) UnmarshalKeyExact(key string, rawVal interface{}) error { + config := defaultDecoderConfig(rawVal) + config.ErrorUnused = true + + err := decode(v.Get(key), config) + + if err != nil { + return err + } + + v.insensitiviseMaps() + + return nil +} // BindPFlags binds a full flag set to the configuration, using each flag's long // name as the config key. From eb4fbe638fa59a5a9fff50a3ff0f7f023c720b04 Mon Sep 17 00:00:00 2001 From: Kolbe Kegel Date: Tue, 22 Aug 2017 23:48:57 -0700 Subject: [PATCH 2/2] Added TestUnmarshalKeyExact() to test UnmarshalKeyExact(). --- viper_test.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/viper_test.go b/viper_test.go index 774ca11..7cd832c 100644 --- a/viper_test.go +++ b/viper_test.go @@ -287,6 +287,37 @@ func TestUnmarshalExact(t *testing.T) { } } +func TestUnmarshalKeyExact(t *testing.T) { + type duration struct { + Delay time.Duration + } + + type item struct { + Name string + Delay time.Duration + Nested duration + } + + config := `invalid_but_irrelevant=true + [[parent]] + error=true + delay="100ms" + [parent.nested] + delay="200ms" +` + initConfig("toml", config) + + var items []item + err := v.UnmarshalKeyExact("parent", &items) + if err == nil { + t.Fatal("UnmarshalKeyExact should error when populating a struct from a conf that contains unused fields") + } + + assert.Equal(t, 1, len(items)) + assert.Equal(t, 100*time.Millisecond, items[0].Delay) + assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay) +} + func TestOverrides(t *testing.T) { Set("age", 40) assert.Equal(t, 40, Get("age"))