Skip to content

Commit bb266b4

Browse files
authored
Merge pull request #2 from msabramo/pr-99-show-type-for-ErrUnsupportedPtrType
Show type for ErrUnsupportedPtrType
2 parents 339909d + a3b3bb2 commit bb266b4

File tree

2 files changed

+77
-11
lines changed

2 files changed

+77
-11
lines changed

request.go

+22-9
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,24 @@ var (
2727
// (numeric) but the Struct field was a non numeric type (i.e. not int, uint,
2828
// float, etc)
2929
ErrUnknownFieldNumberType = errors.New("The struct field was not of a known number type")
30-
// ErrUnsupportedPtrType is returned when the Struct field was a pointer but
31-
// the JSON value was of a different type
32-
ErrUnsupportedPtrType = errors.New("Pointer type in struct is not supported")
3330
// ErrInvalidType is returned when the given type is incompatible with the expected type.
3431
ErrInvalidType = errors.New("Invalid type provided") // I wish we used punctuation.
3532
)
3633

34+
// ErrUnsupportedPtrType is returned when the Struct field was a pointer but
35+
// the JSON value was of a different type
36+
func ErrUnsupportedPtrType(rf reflect.Value, t reflect.Type, structField reflect.StructField) error {
37+
typeName := t.Elem().Name()
38+
kind := t.Elem().Kind()
39+
if kind.String() != "" && kind.String() != typeName {
40+
typeName = fmt.Sprintf("%s (%s)", typeName, kind.String())
41+
}
42+
return fmt.Errorf(
43+
"jsonapi: Can't unmarshal %+v (%s) to struct field `%s`, which is a pointer to `%s`",
44+
rf, rf.Type().Kind(), structField.Name, typeName,
45+
)
46+
}
47+
3748
// UnmarshalPayload converts an io into a struct instance using jsonapi tags on
3849
// struct fields. This method supports single request payloads only, at the
3950
// moment. Bulk creates and updates are not supported yet.
@@ -256,7 +267,8 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node)
256267
continue
257268
}
258269

259-
value, err := unmarshalAttribute(attribute, args, fieldType.Type, fieldValue)
270+
structField := fieldType
271+
value, err := unmarshalAttribute(attribute, args, structField, fieldValue)
260272
if err != nil {
261273
er = err
262274
break
@@ -363,9 +375,10 @@ func assign(field, value reflect.Value) {
363375
}
364376
}
365377

366-
func unmarshalAttribute(attribute interface{}, args []string, fieldType reflect.Type, fieldValue reflect.Value) (value reflect.Value, err error) {
378+
func unmarshalAttribute(attribute interface{}, args []string, structField reflect.StructField, fieldValue reflect.Value) (value reflect.Value, err error) {
367379

368380
value = reflect.ValueOf(attribute)
381+
fieldType := structField.Type
369382

370383
// Handle field of type []string
371384
if fieldValue.Type() == reflect.TypeOf([]string{}) {
@@ -399,7 +412,7 @@ func unmarshalAttribute(attribute interface{}, args []string, fieldType reflect.
399412

400413
// Field was a Pointer type
401414
if fieldValue.Kind() == reflect.Ptr {
402-
value, err = handlePointer(attribute, args, fieldType, fieldValue)
415+
value, err = handlePointer(attribute, args, fieldType, fieldValue, structField)
403416
return
404417
}
405418

@@ -527,7 +540,7 @@ func handleNumeric(attribute interface{}, args []string, fieldType reflect.Type,
527540
return numericValue, nil
528541
}
529542

530-
func handlePointer(attribute interface{}, args []string, fieldType reflect.Type, fieldValue reflect.Value) (reflect.Value, error) {
543+
func handlePointer(attribute interface{}, args []string, fieldType reflect.Type, fieldValue reflect.Value, structField reflect.StructField) (reflect.Value, error) {
531544
t := fieldValue.Type()
532545
var concreteVal reflect.Value
533546

@@ -543,11 +556,11 @@ func handlePointer(attribute interface{}, args []string, fieldType reflect.Type,
543556
case uintptr:
544557
concreteVal = reflect.ValueOf(&cVal)
545558
default:
546-
return reflect.Value{}, ErrUnsupportedPtrType
559+
return reflect.Value{}, ErrUnsupportedPtrType(reflect.ValueOf(attribute), fieldType, structField)
547560
}
548561

549562
if t != concreteVal.Type() {
550-
return reflect.Value{}, ErrUnsupportedPtrType
563+
return reflect.Value{}, ErrUnsupportedPtrType(reflect.ValueOf(attribute), fieldType, structField)
551564
}
552565

553566
return concreteVal, nil

request_test.go

+55-2
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,65 @@ func TestUnmarshalPayloadWithPointerAttr_AbsentVal(t *testing.T) {
121121
}
122122
}
123123

124-
func TestUnmarshalToStructWithPointerAttr_BadType(t *testing.T) {
124+
func TestUnmarshalToStructWithPointerAttr_BadType_bool(t *testing.T) {
125125
out := new(WithPointer)
126126
in := map[string]interface{}{
127127
"name": true, // This is the wrong type.
128128
}
129-
expectedErrorMessage := ErrUnsupportedPtrType.Error()
129+
expectedErrorMessage := "jsonapi: Can't unmarshal true (bool) to struct field `Name`, which is a pointer to `string`"
130+
131+
err := UnmarshalPayload(sampleWithPointerPayload(in), out)
132+
133+
if err == nil {
134+
t.Fatalf("Expected error due to invalid type.")
135+
}
136+
if err.Error() != expectedErrorMessage {
137+
t.Fatalf("Unexpected error message: %s", err.Error())
138+
}
139+
}
140+
141+
func TestUnmarshalToStructWithPointerAttr_BadType_MapPtr(t *testing.T) {
142+
out := new(WithPointer)
143+
in := map[string]interface{}{
144+
"name": &map[string]interface{}{"a": 5}, // This is the wrong type.
145+
}
146+
expectedErrorMessage := "jsonapi: Can't unmarshal map[a:5] (map) to struct field `Name`, which is a pointer to `string`"
147+
148+
err := UnmarshalPayload(sampleWithPointerPayload(in), out)
149+
150+
if err == nil {
151+
t.Fatalf("Expected error due to invalid type.")
152+
}
153+
if err.Error() != expectedErrorMessage {
154+
t.Fatalf("Unexpected error message: %s", err.Error())
155+
}
156+
}
157+
158+
func TestUnmarshalToStructWithPointerAttr_BadType_Struct(t *testing.T) {
159+
out := new(WithPointer)
160+
type FooStruct struct{ A int }
161+
in := map[string]interface{}{
162+
"name": FooStruct{A: 5}, // This is the wrong type.
163+
}
164+
expectedErrorMessage := "jsonapi: Can't unmarshal map[A:5] (map) to struct field `Name`, which is a pointer to `string`"
165+
166+
err := UnmarshalPayload(sampleWithPointerPayload(in), out)
167+
168+
if err == nil {
169+
t.Fatalf("Expected error due to invalid type.")
170+
}
171+
if err.Error() != expectedErrorMessage {
172+
t.Fatalf("Unexpected error message: %s", err.Error())
173+
}
174+
}
175+
176+
func TestUnmarshalToStructWithPointerAttr_BadType_IntSlice(t *testing.T) {
177+
out := new(WithPointer)
178+
type FooStruct struct{ A, B int }
179+
in := map[string]interface{}{
180+
"name": []int{4, 5}, // This is the wrong type.
181+
}
182+
expectedErrorMessage := "jsonapi: Can't unmarshal [4 5] (slice) to struct field `Name`, which is a pointer to `string`"
130183

131184
err := UnmarshalPayload(sampleWithPointerPayload(in), out)
132185

0 commit comments

Comments
 (0)