Skip to content

Commit

Permalink
add initIfSet option
Browse files Browse the repository at this point in the history
  • Loading branch information
sethpollack committed Oct 8, 2024
1 parent 1cb1967 commit e65b4f9
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 1 deletion.
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)
}

0 comments on commit e65b4f9

Please sign in to comment.