Skip to content
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
41 changes: 36 additions & 5 deletions env.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ type Options struct {
// Prefix define a prefix for every key.
Prefix string

// AutoPrefix automatically uses the parsed field key for prefix only when no prefix defined
AutoPrefix bool

// PrefixSeparator automatically uses provided prefix for nested key lookups
PrefixSeparator string

// UseFieldNameByDefault defines whether or not `env` should use the field
// name by default if the `env` key is missing.
// Note that the field name will be "converted" to conform with environment
Expand Down Expand Up @@ -249,6 +255,8 @@ func optionsWithSliceEnvPrefix(opts Options, index int) Options {
RequiredIfNoDef: opts.RequiredIfNoDef,
OnSet: opts.OnSet,
Prefix: fmt.Sprintf("%s%d_", opts.Prefix, index),
AutoPrefix: opts.AutoPrefix,
PrefixSeparator: opts.PrefixSeparator,
UseFieldNameByDefault: opts.UseFieldNameByDefault,
SetDefaultsForZeroValuesOnly: opts.SetDefaultsForZeroValuesOnly,
FuncMap: opts.FuncMap,
Expand All @@ -264,7 +272,9 @@ func optionsWithEnvPrefix(field reflect.StructField, opts Options) Options {
DefaultValueTagName: opts.DefaultValueTagName,
RequiredIfNoDef: opts.RequiredIfNoDef,
OnSet: opts.OnSet,
Prefix: opts.Prefix + field.Tag.Get(opts.PrefixTagName),
Prefix: opts.Prefix + parseOwnPrefix(field, opts),
AutoPrefix: opts.AutoPrefix,
PrefixSeparator: opts.PrefixSeparator,
UseFieldNameByDefault: opts.UseFieldNameByDefault,
SetDefaultsForZeroValuesOnly: opts.SetDefaultsForZeroValuesOnly,
FuncMap: opts.FuncMap,
Expand Down Expand Up @@ -438,8 +448,12 @@ func isSliceOfStructs(refTypeField reflect.StructField) bool {
}

func doParseSlice(ref reflect.Value, processField processFieldFn, opts Options) error {
if opts.Prefix != "" && !strings.HasSuffix(opts.Prefix, string(underscore)) {
opts.Prefix += string(underscore)
var separator = string(underscore)
if opts.PrefixSeparator != "" {
separator = opts.PrefixSeparator
}
if opts.Prefix != "" && !strings.HasSuffix(opts.Prefix, separator) {
opts.Prefix += separator
}

var environments []string
Expand All @@ -453,7 +467,7 @@ func doParseSlice(ref reflect.Value, processField processFieldFn, opts Options)
counter := 0
for finished := false; !finished; {
finished = true
prefix := fmt.Sprintf("%s%d%c", opts.Prefix, counter, underscore)
prefix := opts.Prefix + strconv.Itoa(counter) + separator
for _, variable := range environments {
if strings.HasPrefix(variable, prefix) {
counter++
Expand Down Expand Up @@ -550,11 +564,28 @@ type FieldParams struct {
Ignored bool
}

func parseFieldParams(field reflect.StructField, opts Options) (FieldParams, error) {
func parseOwnPrefix(field reflect.StructField, opts Options) string {
var ownPrefix string
ownPrefix = field.Tag.Get(opts.PrefixTagName)
if ownPrefix == "" && opts.AutoPrefix {
ownPrefix, _ = doParseKeyForOption(field, opts)
}
if opts.PrefixSeparator != "" && !strings.HasSuffix(ownPrefix, opts.PrefixSeparator) {
ownPrefix += opts.PrefixSeparator
}
return ownPrefix
}

func doParseKeyForOption(field reflect.StructField, opts Options) (string, []string) {
ownKey, tags := parseKeyForOption(field.Tag.Get(opts.TagName))
if ownKey == "" && opts.UseFieldNameByDefault {
ownKey = toEnvName(field.Name)
}
return ownKey, tags
}

func parseFieldParams(field reflect.StructField, opts Options) (FieldParams, error) {
ownKey, tags := doParseKeyForOption(field, opts)

defaultValue, hasDefaultValue := field.Tag.Lookup(opts.DefaultValueTagName)

Expand Down
27 changes: 27 additions & 0 deletions env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2411,3 +2411,30 @@ func TestEnvBleed(t *testing.T) {
isEqual(t, "", cfg.Foo)
})
}

func TestAutoPrefix(t *testing.T) {
type Test struct {
Str string `env:"TEST"`
}
type ComplexConfig struct {
Foo *Test `env:"FOO,init"`
Bar Test `envPrefix:"BAR"`
Baz Test `envPrefix:"NOT_FOO_"`
List []Test
Clean *Test
}

t.Setenv("FOO_TEST", "kek")
t.Setenv("BAR_TEST", "pep")
t.Setenv("NOT_FOO_TEST", "lel")
t.Setenv("LIST_0_TEST", "mem1")
t.Setenv("LIST_1_TEST", "mem2")

cfg := ComplexConfig{}
isNoErr(t, ParseWithOptions(&cfg, Options{AutoPrefix: true, PrefixSeparator: "_", UseFieldNameByDefault: true}))
isEqual(t, "kek", cfg.Foo.Str)
isEqual(t, "pep", cfg.Bar.Str)
isEqual(t, "lel", cfg.Baz.Str)
isEqual(t, "mem1", cfg.List[0].Str)
isEqual(t, "mem2", cfg.List[1].Str)
}
Loading