Skip to content

Commit

Permalink
Merge pull request #25 from albenik/struct-unmarshaler
Browse files Browse the repository at this point in the history
Unmarshaler interface can be used with struct now
  • Loading branch information
vrischmann authored Jun 25, 2020
2 parents cf601e2 + 0382b00 commit dc3c743
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 17 deletions.
13 changes: 4 additions & 9 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
language: go

go:
- 1.5
- 1.6
- 1.7
- 1.8
- 1.9
- "1.10"
- "1.11"
- "1.12"
- tip
- "1.12"
- "1.13"
- "1.14"
- tip
26 changes: 18 additions & 8 deletions envconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,11 @@ func readStruct(value reflect.Value, ctx *context) (nonNil bool, err error) {

for i := 0; i < value.NumField(); i++ {
field := value.Field(i)
name := value.Type().Field(i).Name
fieldType := field.Type()
fieldInfo := value.Type().Field(i)
name := fieldInfo.Name

tag := parseTag(value.Type().Field(i).Tag.Get("envconfig"))
tag := parseTag(fieldInfo.Tag.Get("envconfig"))
if tag.skip || !field.CanSet() {
if !field.CanSet() && !ctx.allowUnexported {
return false, ErrUnexportedField
Expand All @@ -161,16 +163,16 @@ func readStruct(value reflect.Value, ctx *context) (nonNil bool, err error) {
parents = ctx.parents

doRead:
switch field.Kind() {
case reflect.Ptr:
switch {
case field.Kind() == reflect.Ptr && !isUnmarshaler(fieldType):
// it's a pointer, create a new value and restart the switch
if field.IsNil() {
field.Set(reflect.New(field.Type().Elem()))
parents = append(parents, field) // track parent pointers to deallocate if no children are filled in
}
field = field.Elem()
goto doRead
case reflect.Struct:
case field.Kind() == reflect.Struct && !isUnmarshaler(fieldType):
var nonNilIn bool
nonNilIn, err = readStruct(field, &context{
name: combineName(ctx.name, name),
Expand Down Expand Up @@ -267,8 +269,8 @@ func setSliceField(value reflect.Value, str string, ctx *context) error {
}

var (
durationType = reflect.TypeOf(new(time.Duration)).Elem()
unmarshalerType = reflect.TypeOf(new(Unmarshaler)).Elem()
durationType = reflect.TypeOf((*time.Duration)(nil)).Elem()
unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
)

func isDurationField(t reflect.Type) bool {
Expand Down Expand Up @@ -323,7 +325,15 @@ func parseValue(v reflect.Value, str string, ctx *context) (err error) {
}

func parseWithUnmarshaler(v reflect.Value, str string) error {
var u = v.Addr().Interface().(Unmarshaler)
var u Unmarshaler
if v.Kind() == reflect.Ptr {
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
u = v.Interface().(Unmarshaler)
} else {
u = v.Addr().Interface().(Unmarshaler)
}
return u.Unmarshal(str)
}

Expand Down
11 changes: 11 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
module github.com/vrischmann/envconfig

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/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
)
25 changes: 25 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
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/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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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/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=
48 changes: 48 additions & 0 deletions structunmarshaler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package envconfig_test

import (
"os"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/vrischmann/envconfig"
)

type dateUnmarshaller time.Time

func (d *dateUnmarshaller) Unmarshal(s string) error {
t, err := time.ParseInLocation("2006-01-02", s, time.UTC)
if err != nil {
return err
}
*d = dateUnmarshaller(t)
return nil
}

func TestParseStructWithUnmarshaler(t *testing.T) {
test := func(conf *struct{ TestEnvDate *dateUnmarshaller }) {
if assert.NoError(t, envconfig.Init(&conf)) {
assert.Equal(t, time.Date(2020, time.June, 20, 0, 0, 0, 0, time.UTC), time.Time(*conf.TestEnvDate))
}
}

os.Setenv("TEST_ENV_DATE", "2020-06-20")

t.Run("val", func(t *testing.T) {
conf := struct {
TestEnvDate dateUnmarshaller
}{}
if assert.NoError(t, envconfig.Init(&conf)) {
assert.Equal(t, time.Date(2020, time.June, 20, 0, 0, 0, 0, time.UTC), time.Time(conf.TestEnvDate))
}
})

t.Run("nil", func(t *testing.T) {
test(&struct{ TestEnvDate *dateUnmarshaller }{})
})

t.Run("nonnil", func(t *testing.T) {
test(&struct{ TestEnvDate *dateUnmarshaller }{TestEnvDate: new(dateUnmarshaller)})
})
}

0 comments on commit dc3c743

Please sign in to comment.