Skip to content

Refactor ptr error and cleanup #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 70 additions & 34 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,33 @@ var (
ErrUnknownFieldNumberType = errors.New("The struct field was not of a known number type")
// ErrInvalidType is returned when the given type is incompatible with the expected type.
ErrInvalidType = errors.New("Invalid type provided") // I wish we used punctuation.

)

// ErrUnsupportedPtrType is returned when the Struct field was a pointer but
// the JSON value was of a different type
func ErrUnsupportedPtrType(rf reflect.Value, t reflect.Type, structField reflect.StructField) error {
typeName := t.Elem().Name()
kind := t.Elem().Kind()
type ErrUnsupportedPtrType struct {
rf reflect.Value
t reflect.Type
structField reflect.StructField
}

func (eupt ErrUnsupportedPtrType) Error() string {
typeName := eupt.t.Elem().Name()
kind := eupt.t.Elem().Kind()
if kind.String() != "" && kind.String() != typeName {
typeName = fmt.Sprintf("%s (%s)", typeName, kind.String())
}
return fmt.Errorf(
return fmt.Sprintf(
"jsonapi: Can't unmarshal %+v (%s) to struct field `%s`, which is a pointer to `%s`",
rf, rf.Type().Kind(), structField.Name, typeName,
eupt.rf, eupt.rf.Type().Kind(), eupt.structField.Name, typeName,
)
}

func newErrUnsupportedPtrType(rf reflect.Value, t reflect.Type, structField reflect.StructField) error {
return ErrUnsupportedPtrType{rf, t, structField}
}

// UnmarshalPayload converts an io into a struct instance using jsonapi tags on
// struct fields. This method supports single request payloads only, at the
// moment. Bulk creates and updates are not supported yet.
Expand Down Expand Up @@ -129,7 +140,6 @@ func UnmarshalManyPayload(in io.Reader, t reflect.Type) ([]interface{}, error) {
}

func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node) (err error) {

defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("data is not a jsonapi representation of '%v'", model.Type())
Expand Down Expand Up @@ -375,8 +385,11 @@ func assign(field, value reflect.Value) {
}
}

func unmarshalAttribute(attribute interface{}, args []string, structField reflect.StructField, fieldValue reflect.Value) (value reflect.Value, err error) {

func unmarshalAttribute(
attribute interface{},
args []string,
structField reflect.StructField,
fieldValue reflect.Value) (value reflect.Value, err error) {
value = reflect.ValueOf(attribute)
fieldType := structField.Type

Expand All @@ -387,7 +400,8 @@ func unmarshalAttribute(attribute interface{}, args []string, structField reflec
}

// Handle field of type time.Time
if fieldValue.Type() == reflect.TypeOf(time.Time{}) || fieldValue.Type() == reflect.TypeOf(new(time.Time)) {
if fieldValue.Type() == reflect.TypeOf(time.Time{}) ||
fieldValue.Type() == reflect.TypeOf(new(time.Time)) {
value, err = handleTime(attribute, args, fieldType, fieldValue)
return
}
Expand All @@ -399,7 +413,8 @@ func unmarshalAttribute(attribute interface{}, args []string, structField reflec
}

// Handle field containing slice of structs
if fieldValue.Type().Kind() == reflect.Slice && reflect.TypeOf(fieldValue.Interface()).Elem().Kind() == reflect.Struct {
if fieldValue.Type().Kind() == reflect.Slice &&
reflect.TypeOf(fieldValue.Interface()).Elem().Kind() == reflect.Struct {
value, err = handleStructSlice(attribute, args, fieldType, fieldValue)
return
}
Expand All @@ -425,7 +440,11 @@ func unmarshalAttribute(attribute interface{}, args []string, structField reflec
return
}

func handleStringSlice(attribute interface{}, args []string, fieldType reflect.Type, fieldValue reflect.Value) (reflect.Value, error) {
func handleStringSlice(
attribute interface{},
args []string,
fieldType reflect.Type,
fieldValue reflect.Value) (reflect.Value, error) {
v := reflect.ValueOf(attribute)
values := make([]string, v.Len())
for i := 0; i < v.Len(); i++ {
Expand All @@ -435,8 +454,11 @@ func handleStringSlice(attribute interface{}, args []string, fieldType reflect.T
return reflect.ValueOf(values), nil
}

func handleTime(attribute interface{}, args []string, fieldType reflect.Type, fieldValue reflect.Value) (reflect.Value, error) {

func handleTime(
attribute interface{},
args []string,
fieldType reflect.Type,
fieldValue reflect.Value) (reflect.Value, error) {
var isIso8601 bool
v := reflect.ValueOf(attribute)

Expand Down Expand Up @@ -483,7 +505,11 @@ func handleTime(attribute interface{}, args []string, fieldType reflect.Type, fi
return reflect.ValueOf(t), nil
}

func handleNumeric(attribute interface{}, args []string, fieldType reflect.Type, fieldValue reflect.Value) (reflect.Value, error) {
func handleNumeric(
attribute interface{},
args []string,
fieldType reflect.Type,
fieldValue reflect.Value) (reflect.Value, error) {
v := reflect.ValueOf(attribute)
floatValue := v.Interface().(float64)

Expand Down Expand Up @@ -540,7 +566,12 @@ func handleNumeric(attribute interface{}, args []string, fieldType reflect.Type,
return numericValue, nil
}

func handlePointer(attribute interface{}, args []string, fieldType reflect.Type, fieldValue reflect.Value, structField reflect.StructField) (reflect.Value, error) {
func handlePointer(
attribute interface{},
args []string,
fieldType reflect.Type,
fieldValue reflect.Value,
structField reflect.StructField) (reflect.Value, error) {
t := fieldValue.Type()
var concreteVal reflect.Value

Expand All @@ -549,50 +580,55 @@ func handlePointer(attribute interface{}, args []string, fieldType reflect.Type,
concreteVal = reflect.ValueOf(&cVal)
case bool:
concreteVal = reflect.ValueOf(&cVal)
case complex64:
concreteVal = reflect.ValueOf(&cVal)
case complex128:
concreteVal = reflect.ValueOf(&cVal)
case uintptr:
case complex64, complex128, uintptr:
concreteVal = reflect.ValueOf(&cVal)
case map[string]interface{}:
var err error
concreteVal, err = handleStruct(attribute, args, fieldType, fieldValue)
if err != nil {
return reflect.Value{}, ErrUnsupportedPtrType(reflect.ValueOf(attribute), fieldType, structField)
return reflect.Value{}, newErrUnsupportedPtrType(
reflect.ValueOf(attribute), fieldType, structField)
}
return concreteVal.Elem(), err
default:
return reflect.Value{}, ErrUnsupportedPtrType(reflect.ValueOf(attribute), fieldType, structField)
return reflect.Value{}, newErrUnsupportedPtrType(
reflect.ValueOf(attribute), fieldType, structField)
}

if t != concreteVal.Type() {
return reflect.Value{}, ErrUnsupportedPtrType(reflect.ValueOf(attribute), fieldType, structField)
return reflect.Value{}, newErrUnsupportedPtrType(
reflect.ValueOf(attribute), fieldType, structField)
}

return concreteVal, nil
}

func handleStruct(attribute interface{}, args []string, fieldType reflect.Type, fieldValue reflect.Value) (reflect.Value, error) {
func handleStruct(
attribute interface{},
args []string,
fieldType reflect.Type,
fieldValue reflect.Value) (reflect.Value, error) {
model := reflect.New(fieldValue.Type())

var er error

data, er := json.Marshal(attribute)
if er != nil {
return model, er
data, err := json.Marshal(attribute)
if err != nil {
return model, err
}

er = json.Unmarshal(data, model.Interface())
err = json.Unmarshal(data, model.Interface())

if er != nil {
return model, er
if err != nil {
return model, err
}

return model, er
return model, err
}

func handleStructSlice(attribute interface{}, args []string, fieldType reflect.Type, fieldValue reflect.Value) (reflect.Value, error) {
func handleStructSlice(
attribute interface{},
args []string,
fieldType reflect.Type,
fieldValue reflect.Value) (reflect.Value, error) {
models := reflect.New(fieldValue.Type()).Elem()
dataMap := reflect.ValueOf(attribute).Interface().([]interface{})
for _, data := range dataMap {
Expand Down
12 changes: 12 additions & 0 deletions request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ func TestUnmarshalToStructWithPointerAttr_BadType_bool(t *testing.T) {
if err.Error() != expectedErrorMessage {
t.Fatalf("Unexpected error message: %s", err.Error())
}
if _, ok := err.(ErrUnsupportedPtrType); !ok {
t.Fatalf("Unexpected error type: %s", reflect.TypeOf(err))
}
}

func TestUnmarshalToStructWithPointerAttr_BadType_MapPtr(t *testing.T) {
Expand All @@ -153,6 +156,9 @@ func TestUnmarshalToStructWithPointerAttr_BadType_MapPtr(t *testing.T) {
if err.Error() != expectedErrorMessage {
t.Fatalf("Unexpected error message: %s", err.Error())
}
if _, ok := err.(ErrUnsupportedPtrType); !ok {
t.Fatalf("Unexpected error type: %s", reflect.TypeOf(err))
}
}

func TestUnmarshalToStructWithPointerAttr_BadType_Struct(t *testing.T) {
Expand All @@ -171,6 +177,9 @@ func TestUnmarshalToStructWithPointerAttr_BadType_Struct(t *testing.T) {
if err.Error() != expectedErrorMessage {
t.Fatalf("Unexpected error message: %s", err.Error())
}
if _, ok := err.(ErrUnsupportedPtrType); !ok {
t.Fatalf("Unexpected error type: %s", reflect.TypeOf(err))
}
}

func TestUnmarshalToStructWithPointerAttr_BadType_IntSlice(t *testing.T) {
Expand All @@ -189,6 +198,9 @@ func TestUnmarshalToStructWithPointerAttr_BadType_IntSlice(t *testing.T) {
if err.Error() != expectedErrorMessage {
t.Fatalf("Unexpected error message: %s", err.Error())
}
if _, ok := err.(ErrUnsupportedPtrType); !ok {
t.Fatalf("Unexpected error type: %s", reflect.TypeOf(err))
}
}

func TestStringPointerField(t *testing.T) {
Expand Down