diff --git a/viper.go b/viper.go index a056e61..7a49a0a 100644 --- a/viper.go +++ b/viper.go @@ -623,6 +623,37 @@ func (v *Viper) Unmarshal(rawVal interface{}) error { return nil } +// A wrapper around mapstructure.Decode that mimics the WeakDecode functionality +// while erroring on non existing vals in the destination struct +func weakDecodeExact(input, output interface{}) error { + config := &mapstructure.DecoderConfig{ + ErrorUnused: true, + Metadata: nil, + Result: output, + WeaklyTypedInput: true, + } + + decoder, err := mapstructure.NewDecoder(config) + if err != nil { + return err + } + return decoder.Decode(input) +} + +// Unmarshals the config into a Struct, erroring if a field is non-existant +// in the destination struct +func (v *Viper) UnmarshalExact(rawVal interface{}) error { + err := weakDecodeExact(v.AllSettings(), rawVal) + + if err != nil { + return err + } + + v.insensitiviseMaps() + + return nil +} + // Bind a full flag set to the configuration, using each flag's long // name as the config key. func BindPFlags(flags *pflag.FlagSet) (err error) { return v.BindPFlags(flags) } diff --git a/viper_test.go b/viper_test.go index ebde5ba..858caff 100644 --- a/viper_test.go +++ b/viper_test.go @@ -37,6 +37,14 @@ eyes : brown beard: true `) +var yamlExampleWithExtras = []byte(`Existing: true +Bogus: true +`) + +type testUnmarshalExtra struct { + Existing bool +} + var tomlExample = []byte(` title = "TOML Example" @@ -255,6 +263,18 @@ func TestUnmarshalling(t *testing.T) { assert.Equal(t, 35, Get("age")) } +func TestUnmarshalExact(t *testing.T) { + vip := New() + target := &testUnmarshalExtra{} + vip.SetConfigType("yaml") + r := bytes.NewReader(yamlExampleWithExtras) + vip.ReadConfig(r) + err := vip.UnmarshalExact(target) + if err == nil { + t.Fatal("UnmarshalExact should error when populating a struct from a conf that contains unused fields") + } +} + func TestOverrides(t *testing.T) { Set("age", 40) assert.Equal(t, 40, Get("age")) @@ -844,14 +864,14 @@ func TestUnmarshalingWithAliases(t *testing.T) { Set("name", "Steve") Set("lastname", "Owen") - RegisterAlias("UserID","Id") - RegisterAlias("Firstname","name") - RegisterAlias("Surname","lastname") + RegisterAlias("UserID", "Id") + RegisterAlias("Firstname", "name") + RegisterAlias("Surname", "lastname") type config struct { - Id int + Id int FirstName string - Surname string + Surname string } var C config @@ -862,4 +882,4 @@ func TestUnmarshalingWithAliases(t *testing.T) { } assert.Equal(t, &C, &config{Id: 1, FirstName: "Steve", Surname: "Owen"}) -} \ No newline at end of file +}