Skip to content

Commit 212b1c4

Browse files
Fix panic when the decoding pointer type with custom decoding function (#68)
## Fixes Or Enhances When a pointer type has a custom decoder and the decoded value is nil, we get a panic. Example ```go func main() { type CustomTime *time.Time type TestError struct { CT CustomTime } var test TestError decoder = form.NewDecoder() decoder.RegisterCustomTypeFunc(func(s []string) (interface{}, error) { if s[0] == "" { return nil, nil } parsed, err := time.Parse(time.RFC3339, s[0]) if err != nil { return nil, err } return CustomTime(&parsed), nil }, CustomTime(nil)) values := url.Values{ "CT": []string{""}, } decoder.Decode(&test, values) } ``` ``` panic: reflect: call of reflect.Value.Set on zero Value ``` This PR specifically handles the case for the case when the type is a pointer. **Make sure that you've checked the boxes below before you submit PR:** - [x] Tests exist or have been written that cover this particular change. @go-playground/admins
1 parent 8785d3c commit 212b1c4

File tree

2 files changed

+57
-7
lines changed

2 files changed

+57
-7
lines changed

decoder.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,15 @@ func (d *decoder) setFieldByType(current reflect.Value, namespace []byte, idx in
192192
d.setError(namespace, err)
193193
return
194194
}
195+
if kind == reflect.Ptr {
196+
newVal := reflect.New(v.Type().Elem())
197+
if set = d.setFieldByType(newVal.Elem(), namespace, idx); set {
198+
v.Set(newVal)
199+
}
200+
} else {
201+
v.Set(reflect.ValueOf(val))
202+
}
195203

196-
v.Set(reflect.ValueOf(val))
197204
set = true
198205
return
199206
}

decoder_test.go

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -830,28 +830,71 @@ func TestDecoderStruct(t *testing.T) {
830830

831831
func TestDecoderNativeTime(t *testing.T) {
832832

833+
type TType time.Time
834+
type TTypePtr *time.Time
835+
type TTypePtrWithCustomDecoder *time.Time
836+
833837
type TestError struct {
834-
Time time.Time
835-
TimeNoValue time.Time
836-
TimePtr *time.Time
838+
Time time.Time
839+
TimeNoValue time.Time
840+
TimePtr *time.Time
841+
TimeType TType
842+
TimeTypeNoValue TType
843+
TimeTypePtr TTypePtr
844+
TimeTypePtrNoValue TTypePtr
845+
TimeTypePtrWithCustomDecoder TTypePtrWithCustomDecoder
846+
TimeTypePtrWithCustomDecoderNoValue TTypePtrWithCustomDecoder
837847
}
838848

839849
values := url.Values{
840-
"Time": []string{"2006-01-02T15:04:05Z"},
841-
"TimeNoValue": []string{""},
842-
"TimePtr": []string{"2006-01-02T15:04:05Z"},
850+
"Time": []string{"2006-01-02T15:04:05Z"},
851+
"TimeNoValue": []string{""},
852+
"TimePtr": []string{"2006-01-02T15:04:05Z"},
853+
"TimeType": []string{"2006-01-02T15:04:05Z"},
854+
"TimeTypeNoValue": []string{""},
855+
"TimeTypePtr": []string{"2006-01-02T15:04:05Z"},
856+
"TimeTypePtrNoValue": []string{""},
857+
"TimeTypePtrWithCustomDecoder": []string{"2006-01-02T15:04:05Z"},
858+
"TimeTypePtrWithCustomDecoderNoValue": []string{""},
843859
}
844860

845861
var test TestError
846862

847863
decoder := NewDecoder()
864+
decoder.RegisterCustomTypeFunc(func(s []string) (interface{}, error) {
865+
if s[0] == "" {
866+
return TType{}, nil
867+
}
868+
parsed, err := time.Parse(time.RFC3339, s[0])
869+
if err != nil {
870+
return nil, err
871+
}
872+
return TType(parsed), nil
873+
}, TType{})
874+
decoder.RegisterCustomTypeFunc(func(s []string) (interface{}, error) {
875+
if s[0] == "" {
876+
return nil, nil
877+
}
878+
parsed, err := time.Parse(time.RFC3339, s[0])
879+
if err != nil {
880+
return nil, err
881+
}
882+
return TTypePtrWithCustomDecoder(&parsed), nil
883+
}, TTypePtrWithCustomDecoder(nil))
848884

849885
errs := decoder.Decode(&test, values)
850886
Equal(t, errs, nil)
851887

852888
tm, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
853889
Equal(t, test.Time.Equal(tm), true)
854890
Equal(t, test.TimeNoValue.Equal(tm), false)
891+
Equal(t, tm.Equal(time.Time(test.TimeType)), true)
892+
Equal(t, time.Time(test.TimeTypeNoValue).Equal(tm), false)
893+
Equal(t, time.Time(test.TimeTypeNoValue).IsZero(), true)
894+
Equal(t, (*time.Time)(test.TimeTypePtr).Equal(tm), true)
895+
Equal(t, test.TimeTypePtrNoValue, nil)
896+
Equal(t, (*time.Time)(test.TimeTypePtrWithCustomDecoder).Equal(tm), true)
897+
Equal(t, test.TimeTypePtrWithCustomDecoderNoValue, nil)
855898

856899
NotEqual(t, test.TimePtr, nil)
857900
Equal(t, (*test.TimePtr).Equal(tm), true)

0 commit comments

Comments
 (0)