diff --git a/viper.go b/viper.go index 714b2f2..122280b 100644 --- a/viper.go +++ b/viper.go @@ -1179,7 +1179,14 @@ func (v *Viper) UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error { config := defaultDecoderConfig(rawVal, opts...) config.ErrorUnused = true - return decode(v.AllSettings(), config) + // TODO: make this optional? + structKeys, err := v.decodeStructKeys(rawVal, opts...) + if err != nil { + return err + } + + // TODO: struct keys should be enough? + return decode(v.getSettings(append(v.AllKeys(), structKeys...)), config) } // BindPFlags binds a full flag set to the configuration, using each flag's long diff --git a/viper_test.go b/viper_test.go index 98c379d..4dc52ac 100644 --- a/viper_test.go +++ b/viper_test.go @@ -1052,6 +1052,32 @@ func TestUnmarshalWithAutomaticEnv(t *testing.T) { assert.Error(t, err, "expected viper.Unmarshal to return error due to unset field 'FLAG'") }) + + t.Run("Exact", func(t *testing.T) { + var config Configuration + + v.Set("port", 1234) + if err := v.UnmarshalExact(&config); err != nil { + t.Fatalf("unable to decode into struct, %v", err) + } + + assert.Equal( + t, + Configuration{ + Name: "Steve", + Port: 1234, + Duration: time.Second + time.Millisecond, + Modes: []int{1, 2, 3}, + Authentication: AuthConfig{ + Secret: "42", + }, + Storage: StorageConfig{ + Size: 4096, + }, + }, + config, + ) + }) } func TestBindPFlags(t *testing.T) {