Skip to content

Commit a132090

Browse files
author
Dean Karn
authored
support pointers using default tag (#49)
This fixes #47 where `default`, and other set tags, were not setting pointer types.
1 parent 4214131 commit a132090

File tree

6 files changed

+145
-60
lines changed

6 files changed

+145
-60
lines changed

.github/workflows/workflow.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,20 @@ jobs:
88
test:
99
strategy:
1010
matrix:
11-
go-version: [1.20.x, 1.19.x, 1.18.x]
11+
go-version: [1.23.x, 1.22.x, 1.21.x, 1.20.x, 1.19.x, 1.18.x]
1212
os: [ubuntu-latest, macos-latest, windows-latest]
1313
runs-on: ${{ matrix.os }}
1414
steps:
1515
- name: Install Go
16-
uses: actions/setup-go@v3
16+
uses: actions/setup-go@v5
1717
with:
1818
go-version: ${{ matrix.go-version }}
1919

2020
- name: Checkout code
21-
uses: actions/checkout@v3
21+
uses: actions/checkout@v4
2222

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

3434
- name: Send Coverage
35-
if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.20.x'
35+
if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.23.x'
3636
uses: shogo82148/actions-goveralls@v1
3737
with:
3838
path-to-profile: profile.cov
@@ -41,8 +41,8 @@ jobs:
4141
name: lint
4242
runs-on: ubuntu-latest
4343
steps:
44-
- uses: actions/checkout@v3
44+
- uses: actions/checkout@v4
4545
- name: golangci-lint
46-
uses: golangci/golangci-lint-action@v3
46+
uses: golangci/golangci-lint-action@v6
4747
with:
4848
version: latest

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package mold
22
============
3-
![Project status](https://img.shields.io/badge/version-4.5.0-green.svg)
3+
![Project status](https://img.shields.io/badge/version-4.5.1-green.svg)
44
[![Build Status](https://travis-ci.org/go-playground/mold.svg?branch=v2)](https://travis-ci.org/go-playground/mold)
55
[![Coverage Status](https://coveralls.io/repos/github/go-playground/mold/badge.svg?branch=v2)](https://coveralls.io/github/go-playground/mold?branch=v2)
66
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/mold)](https://goreportcard.com/report/github.com/go-playground/mold)

go.mod

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ module github.com/go-playground/mold/v4
33
go 1.18
44

55
require (
6-
github.com/go-playground/assert/v2 v2.0.1
6+
github.com/go-playground/assert/v2 v2.2.0
7+
github.com/gosimple/slug v1.15.0
78
github.com/segmentio/go-camelcase v0.0.0-20160726192923-7085f1e3c734
89
github.com/segmentio/go-snakecase v1.2.0
9-
github.com/stretchr/testify v1.7.0
10-
golang.org/x/text v0.6.0
10+
github.com/stretchr/testify v1.10.0
11+
golang.org/x/text v0.21.0
1112
)
1213

1314
require (
14-
github.com/davecgh/go-spew v1.1.0 // indirect
15-
github.com/gosimple/slug v1.13.1 // indirect
15+
github.com/davecgh/go-spew v1.1.1 // indirect
1616
github.com/gosimple/unidecode v1.0.1 // indirect
1717
github.com/pmezard/go-difflib v1.0.0 // indirect
18-
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
18+
gopkg.in/yaml.v3 v3.0.1 // indirect
1919
)

go.sum

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
2-
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3-
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
4-
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
5-
github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q=
6-
github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
4+
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
5+
github.com/gosimple/slug v1.15.0 h1:wRZHsRrRcs6b0XnxMUBM6WK1U1Vg5B0R7VkIf1Xzobo=
6+
github.com/gosimple/slug v1.15.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
77
github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
88
github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc=
99
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -12,12 +12,11 @@ github.com/segmentio/go-camelcase v0.0.0-20160726192923-7085f1e3c734 h1:Cpx2WLIv
1212
github.com/segmentio/go-camelcase v0.0.0-20160726192923-7085f1e3c734/go.mod h1:hqVOMAwu+ekffC3Tvq5N1ljnXRrFKcaSjbCmQ8JgYaI=
1313
github.com/segmentio/go-snakecase v1.2.0 h1:4cTmEjPGi03WmyAHWBjX53viTpBkn/z+4DO++fqYvpw=
1414
github.com/segmentio/go-snakecase v1.2.0/go.mod h1:jk1miR5MS7Na32PZUykG89Arm+1BUSYhuGR6b7+hJto=
15-
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
16-
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
17-
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
18-
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
19-
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
15+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
16+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
17+
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
18+
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
2019
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2120
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
22-
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
23-
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
21+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
22+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

modifiers/multi.go

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,115 +23,121 @@ func defaultValue(ctx context.Context, fl mold.FieldLevel) error {
2323
return setValue(ctx, fl)
2424
}
2525

26+
func setValue(_ context.Context, fl mold.FieldLevel) error {
27+
return setValueInner(fl.Field(), fl.Param())
28+
}
29+
2630
// setValue allows setting of a specified value
27-
func setValue(ctx context.Context, fl mold.FieldLevel) error {
28-
switch fl.Field().Kind() {
31+
func setValueInner(field reflect.Value, param string) error {
32+
switch field.Kind() {
2933
case reflect.String:
30-
fl.Field().SetString(fl.Param())
34+
field.SetString(param)
3135

3236
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
33-
value, err := strconv.Atoi(fl.Param())
37+
value, err := strconv.Atoi(param)
3438
if err != nil {
3539
return err
3640
}
37-
fl.Field().SetInt(int64(value))
41+
field.SetInt(int64(value))
3842

3943
case reflect.Int64:
4044
var value int64
4145

42-
if fl.Field().Type() == durationType {
43-
d, err := time.ParseDuration(fl.Param())
46+
if field.Type() == durationType {
47+
d, err := time.ParseDuration(param)
4448
if err != nil {
4549
return err
4650
}
4751
value = int64(d)
4852
} else {
49-
i, err := strconv.Atoi(fl.Param())
53+
i, err := strconv.Atoi(param)
5054
if err != nil {
5155
return err
5256
}
5357
value = int64(i)
5458
}
55-
fl.Field().SetInt(value)
59+
field.SetInt(value)
5660

5761
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
58-
value, err := strconv.Atoi(fl.Param())
62+
value, err := strconv.Atoi(param)
5963
if err != nil {
6064
return err
6165
}
62-
fl.Field().SetUint(uint64(value))
66+
field.SetUint(uint64(value))
6367

6468
case reflect.Float32, reflect.Float64:
65-
value, err := strconv.ParseFloat(fl.Param(), 64)
69+
value, err := strconv.ParseFloat(param, 64)
6670
if err != nil {
6771
return err
6872
}
69-
fl.Field().SetFloat(value)
73+
field.SetFloat(value)
7074

7175
case reflect.Bool:
72-
value, err := strconv.ParseBool(fl.Param())
76+
value, err := strconv.ParseBool(param)
7377
if err != nil {
7478
return err
7579
}
76-
fl.Field().SetBool(value)
80+
field.SetBool(value)
7781

7882
case reflect.Map:
7983
var n int
8084
var err error
81-
if fl.Param() != "" {
82-
n, err = strconv.Atoi(fl.Param())
85+
if param != "" {
86+
n, err = strconv.Atoi(param)
8387
if err != nil {
8488
return err
8589
}
8690
}
87-
fl.Field().Set(reflect.MakeMapWithSize(fl.Field().Type(), n))
91+
field.Set(reflect.MakeMapWithSize(field.Type(), n))
8892

8993
case reflect.Slice:
9094
var cap int
9195
var err error
92-
if fl.Param() != "" {
93-
cap, err = strconv.Atoi(fl.Param())
96+
if param != "" {
97+
cap, err = strconv.Atoi(param)
9498
if err != nil {
9599
return err
96100
}
97101
}
98-
fl.Field().Set(reflect.MakeSlice(fl.Field().Type(), 0, cap))
102+
field.Set(reflect.MakeSlice(field.Type(), 0, cap))
99103

100104
case reflect.Struct:
101-
if fl.Field().Type() == timeType {
102-
if fl.Param() != "" {
103-
if strings.ToLower(fl.Param()) == "utc" {
104-
fl.Field().Set(reflect.ValueOf(time.Now().UTC()))
105+
if field.Type() == timeType {
106+
if param != "" {
107+
if strings.ToLower(param) == "utc" {
108+
field.Set(reflect.ValueOf(time.Now().UTC()))
105109
} else {
106-
t, err := time.Parse(time.RFC3339Nano, fl.Param())
110+
t, err := time.Parse(time.RFC3339Nano, param)
107111
if err != nil {
108112
return err
109113
}
110-
fl.Field().Set(reflect.ValueOf(t))
114+
field.Set(reflect.ValueOf(t))
111115
}
112116
} else {
113-
fl.Field().Set(reflect.ValueOf(time.Now()))
117+
field.Set(reflect.ValueOf(time.Now()))
114118
}
115119
}
116120
case reflect.Chan:
117121
var buffer int
118122
var err error
119-
if fl.Param() != "" {
120-
buffer, err = strconv.Atoi(fl.Param())
123+
if param != "" {
124+
buffer, err = strconv.Atoi(param)
121125
if err != nil {
122126
return err
123127
}
124128
}
125-
fl.Field().Set(reflect.MakeChan(fl.Field().Type(), buffer))
129+
field.Set(reflect.MakeChan(field.Type(), buffer))
126130

127131
case reflect.Ptr:
128-
fl.Field().Set(reflect.New(fl.Field().Type().Elem()))
132+
133+
field.Set(reflect.New(field.Type().Elem()))
134+
return setValueInner(field.Elem(), param)
129135
}
130136
return nil
131137
}
132138

133139
// empty sets the field to the zero value of the field type
134-
func empty(ctx context.Context, fl mold.FieldLevel) error {
140+
func empty(_ context.Context, fl mold.FieldLevel) error {
135141
zeroValue := reflect.Zero(fl.Field().Type())
136142
fl.Field().Set(zeroValue)
137143
return nil

modifiers/multi_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,20 @@ func TestDefaultSetSpecialTypes(t *testing.T) {
169169

170170
},
171171
},
172+
{
173+
name: "set *time.Time to value",
174+
field: (*time.Time)(nil),
175+
tags: "set=2023-05-28T15:50:31Z",
176+
vf: func(field interface{}) {
177+
m := field.(time.Time)
178+
Equal(t, m.Location(), time.UTC)
179+
180+
tm, err := time.Parse(time.RFC3339Nano, "2023-05-28T15:50:31Z")
181+
Equal(t, err, nil)
182+
Equal(t, tm.Equal(m), true)
183+
184+
},
185+
},
172186
{
173187
name: "default pointer to slice",
174188
field: (*[]string)(nil),
@@ -186,6 +200,32 @@ func TestDefaultSetSpecialTypes(t *testing.T) {
186200
m := field.([]string)
187201
Equal(t, len(m), 0)
188202
},
203+
}, {
204+
name: "default pointer to int",
205+
field: (*int)(nil),
206+
tags: "default=5",
207+
vf: func(field interface{}) {
208+
m := field.(int)
209+
Equal(t, m, 5)
210+
},
211+
},
212+
{
213+
name: "default pointer to string",
214+
field: (*string)(nil),
215+
tags: "default=test",
216+
vf: func(field interface{}) {
217+
m := field.(string)
218+
Equal(t, m, "test")
219+
},
220+
},
221+
{
222+
name: "set pointer to string",
223+
field: (*string)(nil),
224+
tags: "set",
225+
vf: func(field interface{}) {
226+
m := field.(string)
227+
Equal(t, m, "")
228+
},
189229
},
190230
}
191231

@@ -409,6 +449,42 @@ func TestDefault(t *testing.T) {
409449
tags: "default=blue",
410450
expectError: true,
411451
},
452+
{
453+
name: "default nil pointer to int",
454+
field: (*int)(nil),
455+
tags: "default=3",
456+
expected: 3,
457+
},
458+
{
459+
name: "default not nil pointer to int",
460+
field: newPointer(1),
461+
tags: "default=3",
462+
expected: 1,
463+
},
464+
{
465+
name: "default nil pointer to string",
466+
field: (*string)(nil),
467+
tags: "default=test",
468+
expected: "test",
469+
},
470+
{
471+
name: "default not nil pointer to string",
472+
field: newPointer("existing_value"),
473+
tags: "default=test",
474+
expected: "existing_value",
475+
},
476+
{
477+
name: "default nil pointer to bool",
478+
field: (*bool)(nil),
479+
tags: "default=true",
480+
expected: true,
481+
},
482+
{
483+
name: "default not nil pointer to bool",
484+
field: newPointer(true),
485+
tags: "default=true",
486+
expected: true,
487+
},
412488
}
413489

414490
for _, tc := range tests {
@@ -500,3 +576,7 @@ func TestEmpty(t *testing.T) {
500576
})
501577
}
502578
}
579+
580+
func newPointer[T any](value T) *T {
581+
return &value
582+
}

0 commit comments

Comments
 (0)