Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add initIfSet option #334

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ Here are all the options available for the `env` tag:
- `,expand`: expands environment variables, e.g. `FOO_${BAR}`
- `,file`: instructs that the content of the variable is a path to a file that should be read
- `,init`: initialize nil pointers
- `,initIfSet`: initialize nil pointers when a value is found in the environment.
- `,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
Expand Down
27 changes: 26 additions & 1 deletion env.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ func doParseField(
return err
}

if params.Init && isStructPtr(refField) && refField.IsNil() {
if isStructPtr(refField) && refField.IsNil() && shouldInit(params, optionsWithEnvPrefix(refTypeField, opts)) {
refField.Set(reflect.New(refField.Type().Elem()))
refField = refField.Elem()

Expand All @@ -391,6 +391,18 @@ func doParseField(
return nil
}

func shouldInit(fieldParams FieldParams, opts Options) bool {
if fieldParams.Init {
return true
}

if fieldParams.InitIfSet {
return hasValue(opts)
}

return false
}

func isSliceOfStructs(refTypeField reflect.StructField, opts Options) bool {
field := refTypeField.Type
if reflect.Ptr == field.Kind() {
Expand Down Expand Up @@ -533,6 +545,7 @@ type FieldParams struct {
NotEmpty bool
Expand bool
Init bool
InitIfSet bool
}

func parseFieldParams(field reflect.StructField, opts Options) (FieldParams, error) {
Expand Down Expand Up @@ -567,6 +580,8 @@ func parseFieldParams(field reflect.StructField, opts Options) (FieldParams, err
result.Expand = true
case "init":
result.Init = true
case "initIfSet":
result.InitIfSet = true
default:
return FieldParams{}, newNoSupportedTagOptionError(tag)
}
Expand Down Expand Up @@ -619,6 +634,16 @@ func get(fieldParams FieldParams, opts Options) (val string, err error) {
return val, err
}

// hasValue checks if the struct has any values in the environment variables.
func hasValue(opts Options) bool {
for key, _ := range opts.Environment {
if strings.HasPrefix(key, opts.Prefix) {
return true
}
}
return false
}

// split the env tag's key into the expected key and desired option, if any.
func parseKeyForOption(key string) (string, []string) {
opts := strings.Split(key, ",")
Expand Down
19 changes: 19 additions & 0 deletions env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2214,3 +2214,22 @@ func TestParseWithOptionsRenamedPrefix(t *testing.T) {
isNoErr(t, Parse(cfg))
isEqual(t, "101", cfg.Foo.Str)
}

func TestInitIfSet(t *testing.T) {
type Test struct {
Str string `env:"TEST"`
}
type ComplexConfig struct {
Foo *Test `envPrefix:"FOO_" env:",init"`
Bar *Test `envPrefix:"BAR_" env:",initIfSet"`
Baz *Test `envPrefix:"BAZ_" env:",initIfSet"`
}

t.Setenv("BAR_TEST", "lel")

cfg := ComplexConfig{}
isNoErr(t, Parse(&cfg))
isEqual(t, &Test{}, cfg.Foo)
isEqual(t, &Test{Str: "lel"}, cfg.Bar)
isEqual(t, nil, cfg.Baz)
}