Skip to content

Commit

Permalink
feat: use field name by default (#253)
Browse files Browse the repository at this point in the history
Signed-off-by: Carlos A Becker <caarlos0@users.noreply.github.com>
  • Loading branch information
caarlos0 authored Mar 1, 2023
1 parent 65ba2db commit c08b0f9
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 3 deletions.
29 changes: 26 additions & 3 deletions env.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strconv"
"strings"
"time"
"unicode"
)

// nolint: gochecknoglobals
Expand Down Expand Up @@ -102,15 +103,20 @@ type Options struct {
// TagName specifies another tagname to use rather than the default env.
TagName string

// RequiredIfNoDef automatically sets all env as required if they do not declare 'envDefault'
// RequiredIfNoDef automatically sets all env as required if they do not
// declare 'envDefault'.
RequiredIfNoDef bool

// OnSet allows to run a function when a value is set
// OnSet allows to run a function when a value is set.
OnSet OnSetFn

// Prefix define a prefix for each key
// Prefix define a prefix for each key.
Prefix string

// UseFieldNameByDefault defines whether or not env should use the field
// name by default if the `env` key is missing.
UseFieldNameByDefault bool

// Sets to true if we have already configured once.
configured bool
}
Expand Down Expand Up @@ -145,6 +151,7 @@ func configure(opts []Options) []Options {
if item.Prefix != "" {
opt.Prefix = item.Prefix
}
opt.UseFieldNameByDefault = item.UseFieldNameByDefault
opt.RequiredIfNoDef = item.RequiredIfNoDef
}

Expand Down Expand Up @@ -243,6 +250,19 @@ func doParseField(refField reflect.Value, refTypeField reflect.StructField, func
return nil
}

const underscore rune = '_'

func toEnvName(input string) string {
var output []rune
for i, c := range input {
if i > 0 && output[i-1] != underscore && c != underscore && unicode.ToUpper(c) == c {
output = append(output, underscore)
}
output = append(output, unicode.ToUpper(c))
}
return string(output)
}

func get(field reflect.StructField, opts []Options) (val string, err error) {
var exists bool
var isDefault bool
Expand All @@ -253,6 +273,9 @@ func get(field reflect.StructField, opts []Options) (val string, err error) {
required := opts[0].RequiredIfNoDef
prefix := opts[0].Prefix
ownKey, tags := parseKeyForOption(field.Tag.Get(getTagName(opts)))
if ownKey == "" && opts[0].UseFieldNameByDefault {
ownKey = toEnvName(field.Name)
}
key := prefix + ownKey
for _, tag := range tags {
switch tag {
Expand Down
31 changes: 31 additions & 0 deletions env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1691,6 +1691,37 @@ func TestComplePrefix(t *testing.T) {
isEqual(t, "blahhh", cfg.Blah)
}

func TestNoEnvKey(t *testing.T) {
type Config struct {
Foo string
FooBar string
bar string
}
var cfg Config
isNoErr(t, Parse(&cfg, Options{
UseFieldNameByDefault: true,
Environment: map[string]string{
"FOO": "fooval",
"FOO_BAR": "foobarval",
},
}))
isEqual(t, "fooval", cfg.Foo)
isEqual(t, "foobarval", cfg.FooBar)
isEqual(t, "", cfg.bar)
}

func TestToEnv(t *testing.T) {
for in, out := range map[string]string{
"Foo": "FOO",
"FooBar": "FOO_BAR",
"fooBar": "FOO_BAR",
"Foo_Bar": "FOO_BAR",
"Foo__Bar": "FOO__BAR",
} {
isEqual(t, out, toEnvName(in))
}
}

func isTrue(tb testing.TB, b bool) {
tb.Helper()

Expand Down

0 comments on commit c08b0f9

Please sign in to comment.