diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b6a1d76..29efa44 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,10 +4,10 @@ on: [push, pull_request] jobs: build: - runs-on: ubuntu-16.04 + runs-on: ubuntu-20.04 strategy: matrix: - go: [ '1.12', '1.13', '1.14', '1.15' ] + go: [ '1.12', '1.13', '1.14', '1.15', '1.16', '1.17' ] name: Test with go ${{ matrix.go }} steps: - uses: actions/checkout@v2 diff --git a/envconfig.go b/envconfig.go index 9df9228..beeea05 100644 --- a/envconfig.go +++ b/envconfig.go @@ -25,6 +25,8 @@ var ( type context struct { name string + prefix string + prefixCustomNames bool customName string defaultVal string usingDefault bool @@ -44,6 +46,9 @@ type Options struct { // Prefix allows specifying a prefix for each key. Prefix string + // PrefixCustomNames enables prefixes for the custom names set via struct tags + PrefixCustomNames bool + // AllOptional determines whether to not throw errors by default for any key // that is not found. AllOptional=true means errors will not be thrown. AllOptional bool @@ -96,10 +101,12 @@ func InitWithOptions(conf interface{}, opts Options) error { elem := value.Elem() ctx := context{ - name: opts.Prefix, - optional: opts.AllOptional, - leaveNil: opts.LeaveNil, - allowUnexported: opts.AllowUnexported, + name: opts.Prefix, + prefix: opts.Prefix, + prefixCustomNames: opts.PrefixCustomNames, + optional: opts.AllOptional, + leaveNil: opts.LeaveNil, + allowUnexported: opts.AllowUnexported, } switch elem.Kind() { case reflect.Ptr: @@ -175,24 +182,28 @@ func readStruct(value reflect.Value, ctx *context) (nonNil bool, err error) { case field.Kind() == reflect.Struct && !isUnmarshaler(fieldType): var nonNilIn bool nonNilIn, err = readStruct(field, &context{ - name: combineName(ctx.name, name), - optional: ctx.optional || tag.optional, - defaultVal: tag.defaultVal, - parents: parents, - leaveNil: ctx.leaveNil, - allowUnexported: ctx.allowUnexported, + name: combineName(ctx.name, name), + prefix: ctx.prefix, + prefixCustomNames: ctx.prefixCustomNames, + optional: ctx.optional || tag.optional, + defaultVal: tag.defaultVal, + parents: parents, + leaveNil: ctx.leaveNil, + allowUnexported: ctx.allowUnexported, }) nonNil = nonNil || nonNilIn default: var ok bool ok, err = setField(field, &context{ - name: combineName(ctx.name, name), - customName: tag.customName, - optional: ctx.optional || tag.optional, - defaultVal: tag.defaultVal, - parents: parents, - leaveNil: ctx.leaveNil, - allowUnexported: ctx.allowUnexported, + name: combineName(ctx.name, name), + prefix: ctx.prefix, + prefixCustomNames: ctx.prefixCustomNames, + customName: tag.customName, + optional: ctx.optional || tag.optional, + defaultVal: tag.defaultVal, + parents: parents, + leaveNil: ctx.leaveNil, + allowUnexported: ctx.allowUnexported, }) nonNil = nonNil || ok } @@ -460,6 +471,9 @@ func readValue(ctx *context) (string, error) { func makeAllPossibleKeys(ctx *context) (res []string) { if ctx.customName != "" { + if ctx.prefixCustomNames { + return []string{ctx.prefix + "_" + ctx.customName} + } return []string{ctx.customName} } diff --git a/envconfig_test.go b/envconfig_test.go index 0494265..5641de9 100644 --- a/envconfig_test.go +++ b/envconfig_test.go @@ -8,7 +8,9 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/vrischmann/envconfig" ) @@ -628,3 +630,74 @@ func TestSliceOverwrite(t *testing.T) { require.Equal(t, []string{"baz"}, conf.Single) require.Equal(t, []int{3, 4}, conf.More) } + +func TestInitWithPrefix(t *testing.T) { + t.Run("DefaulBehaviour", func(t *testing.T) { + type Node1 struct { + TestEnvNodeFoo1 string `envconfig:"TEST_ENV_NODE_FOO1"` + TestEnvNodeBar1 string + } + + type Root1 struct { + TestEnvFoo1 string `envconfig:"TEST_ENV_FOO1"` + TestEnvBar1 string + Node1 Node1 + } + + expect := Root1{ + TestEnvFoo1: "A1", + TestEnvBar1: "B1", + Node1: Node1{ + TestEnvNodeFoo1: "C1", + TestEnvNodeBar1: "D1", + }, + } + + require.NoError(t, os.Setenv("TEST_ENV_FOO1", expect.TestEnvFoo1)) + require.NoError(t, os.Setenv("TESTPREFIX1_TEST_ENV_BAR1", expect.TestEnvBar1)) + require.NoError(t, os.Setenv("TEST_ENV_NODE_FOO1", expect.Node1.TestEnvNodeFoo1)) + require.NoError(t, os.Setenv("TESTPREFIX1_NODE1_TEST_ENV_NODE_BAR1", expect.Node1.TestEnvNodeBar1)) + + var conf Root1 + err := envconfig.InitWithPrefix(&conf, "TESTPREFIX1") + + require.NoError(t, err) + assert.Equal(t, expect, conf) + }) + + t.Run("AdvancedBehaviour", func(t *testing.T) { + type Node2 struct { + TestEnvNodeFoo2 string `envconfig:"TEST_ENV_NODE_FOO2"` + TestEnvNodeBar2 string + } + + type Root2 struct { + TestEnvFoo2 string `envconfig:"TEST_ENV_FOO2"` + TestEnvBar2 string + Node2 Node2 + } + + expect := Root2{ + TestEnvFoo2: "A2", + TestEnvBar2: "B2", + Node2: Node2{ + TestEnvNodeFoo2: "C2", + TestEnvNodeBar2: "D2", + }, + } + + require.NoError(t, os.Setenv("TESTPREFIX2_TEST_ENV_FOO2", expect.TestEnvFoo2)) + require.NoError(t, os.Setenv("TESTPREFIX2_TEST_ENV_BAR2", expect.TestEnvBar2)) + require.NoError(t, os.Setenv("TESTPREFIX2_TEST_ENV_NODE_FOO2", expect.Node2.TestEnvNodeFoo2)) + require.NoError(t, os.Setenv("TESTPREFIX2_NODE2_TEST_ENV_NODE_BAR2", expect.Node2.TestEnvNodeBar2)) + + var conf Root2 + err := envconfig.InitWithOptions(&conf, envconfig.Options{ + Prefix: "TESTPREFIX2", + PrefixCustomNames: true, + }) + + require.NoError(t, err) + assert.Equal(t, expect, conf) + }) +} diff --git a/go.mod b/go.mod index 26119b0..dceb4f8 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,9 @@ go 1.12 require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + github.com/kr/pretty v0.3.0 // indirect + github.com/rogpeppe/go-internal v1.8.0 // indirect github.com/stretchr/testify v1.6.1 - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect - gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index c26bc2b..50b9c3d 100644 --- a/go.sum +++ b/go.sum @@ -1,25 +1,29 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=