From 7a25c17e27bebe33ed7be5b169a95f61a0d90aae Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Tue, 20 Jun 2023 09:44:08 -0300 Subject: [PATCH] feat!: accept expand as an option to the env tag (#263) BREAKING CHANGE: this replaces the `envExpand` tag with the `expand` option to the `env` tag, to be more consistent with other boolean options. closes #262 Signed-off-by: Carlos A Becker --- README.md | 22 ++++++++++++++++------ env.go | 4 +++- env_test.go | 13 +++++++------ error.go | 2 +- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ed00505..2c56ce0 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ type config struct { IsProduction bool `env:"PRODUCTION"` Hosts []string `env:"HOSTS" envSeparator:":"` Duration time.Duration `env:"DURATION"` - TempFolder string `env:"TEMP_FOLDER" envDefault:"${HOME}/tmp" envExpand:"true"` + TempFolder string `env:"TEMP_FOLDER,expand" envDefault:"${HOME}/tmp"` } func main() { @@ -102,10 +102,6 @@ case of absence of it in the environment. By default, slice types will split the environment value on `,`; you can change this behavior by setting the `envSeparator` tag. -If you set the `envExpand` tag, environment variables (either in `${var}` or -`$var` format) in the string will be replaced according with the actual value -of the variable. - ## Custom Parser Funcs If you have a type that is not supported out of the box by the lib, you are able @@ -168,6 +164,20 @@ type config struct { > value. > This also means that custom parser funcs will not be invoked. +## Expand vars + +If you set the `expand` option, environment variables (either in `${var}` or +`$var` format) in the string will be replaced according with the actual value +of the variable. For example: + +```go +type config struct { + SecretKey string `env:"SECRET_KEY,expand"` +} +``` + +This also works with `envDefault`. + ## Not Empty fields While `required` demands the environment variable to be set, it doesn't check @@ -214,7 +224,7 @@ import ( type config struct { Secret string `env:"SECRET,file"` Password string `env:"PASSWORD,file" envDefault:"/tmp/password"` - Certificate string `env:"CERTIFICATE,file" envDefault:"${CERTIFICATE_FILE}" envExpand:"true"` + Certificate string `env:"CERTIFICATE,file,expand" envDefault:"${CERTIFICATE_FILE}"` } func main() { diff --git a/env.go b/env.go index aa3d114..be8ee53 100644 --- a/env.go +++ b/env.go @@ -252,6 +252,7 @@ func get(field reflect.StructField, opts Options) (val string, err error) { var loadFile bool var unset bool var notEmpty bool + var expand bool required := opts.RequiredIfNoDef ownKey, tags := parseKeyForOption(field.Tag.Get(opts.TagName)) @@ -271,6 +272,8 @@ func get(field reflect.StructField, opts Options) (val string, err error) { unset = true case "notEmpty": notEmpty = true + case "expand": + expand = true default: return "", newNoSupportedTagOptionError(tag) } @@ -278,7 +281,6 @@ func get(field reflect.StructField, opts Options) (val string, err error) { prefix := opts.Prefix key := prefix + ownKey - expand := strings.EqualFold(field.Tag.Get("envExpand"), "true") defaultValue, defExists := field.Tag.Lookup("envDefault") val, exists, isDefault = getOr(key, defaultValue, defExists, opts.Environment) diff --git a/env_test.go b/env_test.go index 6c43c3c..fabbeea 100644 --- a/env_test.go +++ b/env_test.go @@ -933,11 +933,11 @@ func TestErrorRequiredNotSetWithDefault(t *testing.T) { func TestParseExpandOption(t *testing.T) { type config struct { Host string `env:"HOST" envDefault:"localhost"` - Port int `env:"PORT" envDefault:"3000" envExpand:"True"` - SecretKey string `env:"SECRET_KEY" envExpand:"True"` + Port int `env:"PORT,expand" envDefault:"3000"` + SecretKey string `env:"SECRET_KEY,expand"` ExpandKey string `env:"EXPAND_KEY"` - CompoundKey string `env:"HOST_PORT" envDefault:"${HOST}:${PORT}" envExpand:"True"` - Default string `env:"DEFAULT" envDefault:"def1" envExpand:"True"` + CompoundKey string `env:"HOST_PORT,expand" envDefault:"${HOST}:${PORT}"` + Default string `env:"DEFAULT,expand" envDefault:"def1"` } t.Setenv("HOST", "localhost") @@ -1311,6 +1311,7 @@ func ExampleParse() { Home string `env:"HOME,required"` Port int `env:"PORT" envDefault:"3000"` IsProduction bool `env:"PRODUCTION"` + TempFolder string `env:"TEMP_FOLDER,expand" envDefault:"${HOME}/.tmp"` Inner inner } os.Setenv("HOME", "/tmp/fakehome") @@ -1319,7 +1320,7 @@ func ExampleParse() { fmt.Println("failed:", err) } fmt.Printf("%+v", cfg) - // Output: {Home:/tmp/fakehome Port:3000 IsProduction:false Inner:{Foo:foobar}} + // Output: {Home:/tmp/fakehome Port:3000 IsProduction:false TempFolder:/tmp/fakehome/.tmp Inner:{Foo:foobar}} } func ExampleParse_onSet() { @@ -1495,7 +1496,7 @@ func TestFileBadFile(t *testing.T) { func TestFileWithDefault(t *testing.T) { type config struct { - SecretKey string `env:"SECRET_KEY,file" envDefault:"${FILE}" envExpand:"true"` + SecretKey string `env:"SECRET_KEY,file,expand" envDefault:"${FILE}"` } dir := t.TempDir() diff --git a/error.go b/error.go index cfc9766..156ca3e 100644 --- a/error.go +++ b/error.go @@ -89,7 +89,7 @@ func (e NoParserError) Error() string { } // This error occurs when the given tag is not supported -// In-built supported tags: "", "file", "required", "unset", "notEmpty", "envDefault", "envExpand", "envSeparator" +// In-built supported tags: "", "file", "required", "unset", "notEmpty", "expand", "envDefault", "envSeparator" // How to create a custom tag: https://github.com/caarlos0/env#changing-default-tag-name type NoSupportedTagOptionError struct { Tag string