Skip to content

Commit

Permalink
refactor: call bindStruct within Unmarshal
Browse files Browse the repository at this point in the history
  • Loading branch information
krakowski committed Dec 5, 2023
1 parent 711dad7 commit 36b31a1
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 41 deletions.
52 changes: 22 additions & 30 deletions viper.go
Expand Up @@ -1111,6 +1111,10 @@ func Unmarshal(rawVal any, opts ...DecoderConfigOption) error {
}

func (v *Viper) Unmarshal(rawVal any, opts ...DecoderConfigOption) error {
if err := v.bindStruct(rawVal); err != nil {
return err
}

return decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...))
}

Expand Down Expand Up @@ -1224,26 +1228,6 @@ func (v *Viper) BindEnv(input ...string) error {
return nil
}

// BindStruct binds a struct to the environment by extracting all Viper keys
// from it and binding each to a ENV variable.
func BindStruct(input interface{}) error { return v.BindStruct(input) }

func (v *Viper) BindStruct(input interface{}) error {
envKeysMap := map[string]interface{}{}
if err := mapstructure.Decode(input, &envKeysMap); err != nil {
return err
}

structKeys := v.flattenAndMergeMap(map[string]bool{}, envKeysMap, "")
for key, _ := range structKeys {
if err := v.BindEnv(key); err != nil {
return err
}
}

return nil
}

// MustBindEnv wraps BindEnv in a panic.
// If there is an error binding an environment variable, MustBindEnv will
// panic.
Expand All @@ -1255,16 +1239,6 @@ func (v *Viper) MustBindEnv(input ...string) {
}
}

// MustBindStruct wraps BindStruct in a panic.
// If there is an error binding the struct, BindStruct will panic.
func MustBindStruct(input interface{}) { v.MustBindStruct(input) }

func (v *Viper) MustBindStruct(input interface{}) {
if err := v.BindStruct(input); err != nil {
panic(fmt.Sprintf("error while binding struct: %v", err))
}
}

// Given a key, find the value.
//
// Viper will check to see if an alias exists first.
Expand Down Expand Up @@ -2064,6 +2038,24 @@ func (v *Viper) AllKeys() []string {
return a
}

// bindStruct binds a struct to the environment by extracting all Viper keys
// from it and binding each to a ENV variable using BindEnv.
func (v *Viper) bindStruct(input interface{}) error {
envKeysMap := map[string]interface{}{}
if err := mapstructure.Decode(input, &envKeysMap); err != nil {
return err
}

structKeys := v.flattenAndMergeMap(map[string]bool{}, envKeysMap, "")
for key, _ := range structKeys {
if err := v.BindEnv(key); err != nil {
return err
}
}

return nil
}

// flattenAndMergeMap recursively flattens the given map into a map[string]bool
// of key paths (used as a set, easier to manipulate than a []string):
// - each path is merged into a single key string, delimited with v.keyDelim
Expand Down
18 changes: 7 additions & 11 deletions viper_test.go
Expand Up @@ -914,13 +914,13 @@ func TestUnmarshal(t *testing.T) {
)
}

func TestBindStruct(t *testing.T) {
testutil.Setenv(t, "PORT", "1313")
testutil.Setenv(t, "NAME", "Steve")
testutil.Setenv(t, "DURATION", "1s1ms")
testutil.Setenv(t, "MODES", "1,2,3")
testutil.Setenv(t, "SECRET", "42")
testutil.Setenv(t, "FILESYSTEM_SIZE", "4096")
func TestUnmarshalFromEnv(t *testing.T) {
t.Setenv("PORT", "1313")
t.Setenv("NAME", "Steve")
t.Setenv("DURATION", "1s1ms")
t.Setenv("MODES", "1,2,3")
t.Setenv("SECRET", "42")
t.Setenv("FILESYSTEM_SIZE", "4096")

type AuthConfig struct {
Secret string `mapstructure:"SECRET"`
Expand Down Expand Up @@ -956,10 +956,6 @@ func TestBindStruct(t *testing.T) {
localViper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))

var config Configuration
if err := localViper.BindStruct(&config); err != nil {
t.Fatalf("unable to bind struct, %v", err)
}

if err := localViper.Unmarshal(&config); err != nil {
t.Fatalf("unable to decode into struct, %v", err)
}
Expand Down

0 comments on commit 36b31a1

Please sign in to comment.