diff --git a/README.md b/README.md index de6f8249..16cfc58f 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ Here are all the options available for the `env` tag: - `,notEmpty`: make the field errors if the environment variable is empty - `,required`: make the field errors if the environment variable is not set - `,unset`: unset the environment variable after use +- `,emptyOverridesDefault`: treats an empty environment variable value as an explicit override of the default value ### Parse Options diff --git a/env.go b/env.go index c928b66d..de46a824 100644 --- a/env.go +++ b/env.go @@ -537,17 +537,18 @@ func toEnvName(input string) string { // FieldParams contains information about parsed field tags. type FieldParams struct { - OwnKey string - Key string - DefaultValue string - HasDefaultValue bool - Required bool - LoadFile bool - Unset bool - NotEmpty bool - Expand bool - Init bool - Ignored bool + OwnKey string + Key string + DefaultValue string + HasDefaultValue bool + Required bool + LoadFile bool + Unset bool + NotEmpty bool + Expand bool + Init bool + Ignored bool + EmptyOverridesDefault bool } func parseFieldParams(field reflect.StructField, opts Options) (FieldParams, error) { @@ -583,6 +584,8 @@ func parseFieldParams(field reflect.StructField, opts Options) (FieldParams, err result.Expand = true case "init": result.Init = true + case "emptyOverridesDefault": + result.EmptyOverridesDefault = true case "-": result.Ignored = true default: @@ -600,6 +603,7 @@ func get(fieldParams FieldParams, opts Options) (val string, err error) { fieldParams.Key, fieldParams.DefaultValue, fieldParams.HasDefaultValue, + fieldParams.EmptyOverridesDefault, opts.Environment, ) @@ -648,12 +652,12 @@ func getFromFile(filename string) (value string, err error) { return string(b), err } -func getOr(key, defaultValue string, defExists bool, envs map[string]string) (val string, exists, isDefault bool) { +func getOr(key, defaultValue string, defExists bool, emptyOverridesDefault bool, envs map[string]string) (val string, exists, isDefault bool) { value, exists := envs[key] switch { case (!exists || key == "") && defExists: return defaultValue, true, true - case exists && value == "" && defExists: + case exists && value == "" && !emptyOverridesDefault && defExists: return defaultValue, true, true case !exists: return "", false, false diff --git a/env_test.go b/env_test.go index 9a348f83..fe5259fa 100644 --- a/env_test.go +++ b/env_test.go @@ -2411,3 +2411,20 @@ func TestEnvBleed(t *testing.T) { isEqual(t, "", cfg.Foo) }) } + +func TestEmptyOverridesDefault(t *testing.T) { + type Test struct { + Foo string `env:"FOO" envDefault:"foo"` + Bar string `env:"BAR,emptyOverridesDefault" envDefault:"bar"` + NotSet string `env:"NOT_SET,emptyOverridesDefault" envDefault:"default"` + } + + t.Setenv("FOO", "") + t.Setenv("BAR", "") + + var cfg Test + isNoErr(t, Parse(&cfg)) + isEqual(t, "foo", cfg.Foo) + isEqual(t, "", cfg.Bar) + isEqual(t, "default", cfg.NotSet) +}