Skip to content

Commit

Permalink
support pointers using default tag (#49)
Browse files Browse the repository at this point in the history
This fixes #47 where `default`, and other set tags, were not setting
pointer types.
  • Loading branch information
deankarn authored Dec 29, 2024
1 parent 4214131 commit a132090
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 60 deletions.
14 changes: 7 additions & 7 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@ jobs:
test:
strategy:
matrix:
go-version: [1.20.x, 1.19.x, 1.18.x]
go-version: [1.23.x, 1.22.x, 1.21.x, 1.20.x, 1.19.x, 1.18.x]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}

- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Restore Cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-v1-go-${{ hashFiles('**/go.sum') }}
Expand All @@ -32,7 +32,7 @@ jobs:
run: go test -race -covermode=atomic -coverprofile="profile.cov" ./...

- name: Send Coverage
if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.20.x'
if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.23.x'
uses: shogo82148/actions-goveralls@v1
with:
path-to-profile: profile.cov
Expand All @@ -41,8 +41,8 @@ jobs:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v6
with:
version: latest
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package mold
============
![Project status](https://img.shields.io/badge/version-4.5.0-green.svg)
![Project status](https://img.shields.io/badge/version-4.5.1-green.svg)
[![Build Status](https://travis-ci.org/go-playground/mold.svg?branch=v2)](https://travis-ci.org/go-playground/mold)
[![Coverage Status](https://coveralls.io/repos/github/go-playground/mold/badge.svg?branch=v2)](https://coveralls.io/github/go-playground/mold?branch=v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/mold)](https://goreportcard.com/report/github.com/go-playground/mold)
Expand Down
12 changes: 6 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ module github.com/go-playground/mold/v4
go 1.18

require (
github.com/go-playground/assert/v2 v2.0.1
github.com/go-playground/assert/v2 v2.2.0
github.com/gosimple/slug v1.15.0
github.com/segmentio/go-camelcase v0.0.0-20160726192923-7085f1e3c734
github.com/segmentio/go-snakecase v1.2.0
github.com/stretchr/testify v1.7.0
golang.org/x/text v0.6.0
github.com/stretchr/testify v1.10.0
golang.org/x/text v0.21.0
)

require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/gosimple/slug v1.13.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gosimple/unidecode v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
25 changes: 12 additions & 13 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
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/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q=
github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
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/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/gosimple/slug v1.15.0 h1:wRZHsRrRcs6b0XnxMUBM6WK1U1Vg5B0R7VkIf1Xzobo=
github.com/gosimple/slug v1.15.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand All @@ -12,12 +12,11 @@ github.com/segmentio/go-camelcase v0.0.0-20160726192923-7085f1e3c734 h1:Cpx2WLIv
github.com/segmentio/go-camelcase v0.0.0-20160726192923-7085f1e3c734/go.mod h1:hqVOMAwu+ekffC3Tvq5N1ljnXRrFKcaSjbCmQ8JgYaI=
github.com/segmentio/go-snakecase v1.2.0 h1:4cTmEjPGi03WmyAHWBjX53viTpBkn/z+4DO++fqYvpw=
github.com/segmentio/go-snakecase v1.2.0/go.mod h1:jk1miR5MS7Na32PZUykG89Arm+1BUSYhuGR6b7+hJto=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
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/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
72 changes: 39 additions & 33 deletions modifiers/multi.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,115 +23,121 @@ func defaultValue(ctx context.Context, fl mold.FieldLevel) error {
return setValue(ctx, fl)
}

func setValue(_ context.Context, fl mold.FieldLevel) error {
return setValueInner(fl.Field(), fl.Param())
}

// setValue allows setting of a specified value
func setValue(ctx context.Context, fl mold.FieldLevel) error {
switch fl.Field().Kind() {
func setValueInner(field reflect.Value, param string) error {
switch field.Kind() {
case reflect.String:
fl.Field().SetString(fl.Param())
field.SetString(param)

case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
value, err := strconv.Atoi(fl.Param())
value, err := strconv.Atoi(param)
if err != nil {
return err
}
fl.Field().SetInt(int64(value))
field.SetInt(int64(value))

case reflect.Int64:
var value int64

if fl.Field().Type() == durationType {
d, err := time.ParseDuration(fl.Param())
if field.Type() == durationType {
d, err := time.ParseDuration(param)
if err != nil {
return err
}
value = int64(d)
} else {
i, err := strconv.Atoi(fl.Param())
i, err := strconv.Atoi(param)
if err != nil {
return err
}
value = int64(i)
}
fl.Field().SetInt(value)
field.SetInt(value)

case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
value, err := strconv.Atoi(fl.Param())
value, err := strconv.Atoi(param)
if err != nil {
return err
}
fl.Field().SetUint(uint64(value))
field.SetUint(uint64(value))

case reflect.Float32, reflect.Float64:
value, err := strconv.ParseFloat(fl.Param(), 64)
value, err := strconv.ParseFloat(param, 64)
if err != nil {
return err
}
fl.Field().SetFloat(value)
field.SetFloat(value)

case reflect.Bool:
value, err := strconv.ParseBool(fl.Param())
value, err := strconv.ParseBool(param)
if err != nil {
return err
}
fl.Field().SetBool(value)
field.SetBool(value)

case reflect.Map:
var n int
var err error
if fl.Param() != "" {
n, err = strconv.Atoi(fl.Param())
if param != "" {
n, err = strconv.Atoi(param)
if err != nil {
return err
}
}
fl.Field().Set(reflect.MakeMapWithSize(fl.Field().Type(), n))
field.Set(reflect.MakeMapWithSize(field.Type(), n))

case reflect.Slice:
var cap int
var err error
if fl.Param() != "" {
cap, err = strconv.Atoi(fl.Param())
if param != "" {
cap, err = strconv.Atoi(param)
if err != nil {
return err
}
}
fl.Field().Set(reflect.MakeSlice(fl.Field().Type(), 0, cap))
field.Set(reflect.MakeSlice(field.Type(), 0, cap))

case reflect.Struct:
if fl.Field().Type() == timeType {
if fl.Param() != "" {
if strings.ToLower(fl.Param()) == "utc" {
fl.Field().Set(reflect.ValueOf(time.Now().UTC()))
if field.Type() == timeType {
if param != "" {
if strings.ToLower(param) == "utc" {
field.Set(reflect.ValueOf(time.Now().UTC()))
} else {
t, err := time.Parse(time.RFC3339Nano, fl.Param())
t, err := time.Parse(time.RFC3339Nano, param)
if err != nil {
return err
}
fl.Field().Set(reflect.ValueOf(t))
field.Set(reflect.ValueOf(t))
}
} else {
fl.Field().Set(reflect.ValueOf(time.Now()))
field.Set(reflect.ValueOf(time.Now()))
}
}
case reflect.Chan:
var buffer int
var err error
if fl.Param() != "" {
buffer, err = strconv.Atoi(fl.Param())
if param != "" {
buffer, err = strconv.Atoi(param)
if err != nil {
return err
}
}
fl.Field().Set(reflect.MakeChan(fl.Field().Type(), buffer))
field.Set(reflect.MakeChan(field.Type(), buffer))

case reflect.Ptr:
fl.Field().Set(reflect.New(fl.Field().Type().Elem()))

field.Set(reflect.New(field.Type().Elem()))
return setValueInner(field.Elem(), param)
}
return nil
}

// empty sets the field to the zero value of the field type
func empty(ctx context.Context, fl mold.FieldLevel) error {
func empty(_ context.Context, fl mold.FieldLevel) error {
zeroValue := reflect.Zero(fl.Field().Type())
fl.Field().Set(zeroValue)
return nil
Expand Down
80 changes: 80 additions & 0 deletions modifiers/multi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,20 @@ func TestDefaultSetSpecialTypes(t *testing.T) {

},
},
{
name: "set *time.Time to value",
field: (*time.Time)(nil),
tags: "set=2023-05-28T15:50:31Z",
vf: func(field interface{}) {
m := field.(time.Time)
Equal(t, m.Location(), time.UTC)

tm, err := time.Parse(time.RFC3339Nano, "2023-05-28T15:50:31Z")
Equal(t, err, nil)
Equal(t, tm.Equal(m), true)

},
},
{
name: "default pointer to slice",
field: (*[]string)(nil),
Expand All @@ -186,6 +200,32 @@ func TestDefaultSetSpecialTypes(t *testing.T) {
m := field.([]string)
Equal(t, len(m), 0)
},
}, {
name: "default pointer to int",
field: (*int)(nil),
tags: "default=5",
vf: func(field interface{}) {
m := field.(int)
Equal(t, m, 5)
},
},
{
name: "default pointer to string",
field: (*string)(nil),
tags: "default=test",
vf: func(field interface{}) {
m := field.(string)
Equal(t, m, "test")
},
},
{
name: "set pointer to string",
field: (*string)(nil),
tags: "set",
vf: func(field interface{}) {
m := field.(string)
Equal(t, m, "")
},
},
}

Expand Down Expand Up @@ -409,6 +449,42 @@ func TestDefault(t *testing.T) {
tags: "default=blue",
expectError: true,
},
{
name: "default nil pointer to int",
field: (*int)(nil),
tags: "default=3",
expected: 3,
},
{
name: "default not nil pointer to int",
field: newPointer(1),
tags: "default=3",
expected: 1,
},
{
name: "default nil pointer to string",
field: (*string)(nil),
tags: "default=test",
expected: "test",
},
{
name: "default not nil pointer to string",
field: newPointer("existing_value"),
tags: "default=test",
expected: "existing_value",
},
{
name: "default nil pointer to bool",
field: (*bool)(nil),
tags: "default=true",
expected: true,
},
{
name: "default not nil pointer to bool",
field: newPointer(true),
tags: "default=true",
expected: true,
},
}

for _, tc := range tests {
Expand Down Expand Up @@ -500,3 +576,7 @@ func TestEmpty(t *testing.T) {
})
}
}

func newPointer[T any](value T) *T {
return &value
}

0 comments on commit a132090

Please sign in to comment.