diff --git a/env.go b/env.go index c928b66d..bf7c85f8 100644 --- a/env.go +++ b/env.go @@ -502,12 +502,12 @@ func doParseSlice(ref reflect.Value, processField processFieldFn, opts Options) } func setField(refField reflect.Value, refTypeField reflect.StructField, opts Options, fieldParams FieldParams) error { - value, err := get(fieldParams, opts) + value, fromEnv, err := get(fieldParams, opts) if err != nil { return err } - if value != "" && (!opts.SetDefaultsForZeroValuesOnly || refField.IsZero()) { + if fromEnv || (value != "" && (!opts.SetDefaultsForZeroValuesOnly || refField.IsZero())) { return set(refField, refTypeField, value, opts.FuncMap) } @@ -593,7 +593,7 @@ func parseFieldParams(field reflect.StructField, opts Options) (FieldParams, err return result, nil } -func get(fieldParams FieldParams, opts Options) (val string, err error) { +func get(fieldParams FieldParams, opts Options) (val string, fromEnv bool, err error) { var exists, isDefault bool val, exists, isDefault = getOr( @@ -602,6 +602,9 @@ func get(fieldParams FieldParams, opts Options) (val string, err error) { fieldParams.HasDefaultValue, opts.Environment, ) + if _, ok := opts.Environment[fieldParams.Key]; ok && val != "" { + fromEnv = true + } if fieldParams.Expand { val = os.Expand(val, opts.getRawEnv) @@ -614,18 +617,18 @@ func get(fieldParams FieldParams, opts Options) (val string, err error) { } if fieldParams.Required && !exists && fieldParams.OwnKey != "" { - return "", newVarIsNotSetError(fieldParams.Key) + return "", false, newVarIsNotSetError(fieldParams.Key) } if fieldParams.NotEmpty && val == "" { - return "", newEmptyVarError(fieldParams.Key) + return "", false, newEmptyVarError(fieldParams.Key) } if fieldParams.LoadFile && val != "" { filename := val val, err = getFromFile(filename) if err != nil { - return "", newLoadFileContentError(filename, fieldParams.Key, err) + return "", false, newLoadFileContentError(filename, fieldParams.Key, err) } } @@ -634,7 +637,7 @@ func get(fieldParams FieldParams, opts Options) (val string, err error) { opts.OnSet(fieldParams.Key, val, isDefault) } } - return val, err + return val, fromEnv, err } // split the env tag's key into the expected key and desired option, if any. diff --git a/env_test.go b/env_test.go index 9a348f83..762ec0f1 100644 --- a/env_test.go +++ b/env_test.go @@ -2411,3 +2411,18 @@ func TestEnvBleed(t *testing.T) { isEqual(t, "", cfg.Foo) }) } + +func TestParse_Bug364EnvVarOverridesStructValue(t *testing.T) { + t.Setenv("USERNAME", "user1") + + type cfg struct { + Username string `env:"USERNAME" envDefault:"admin"` + } + c := cfg{Username: "root"} + + isNoErr(t, ParseWithOptions(&c, Options{ + SetDefaultsForZeroValuesOnly: true, + })) + + isEqual(t, c.Username, "user1") +}