Skip to content

Commit

Permalink
Refactor to simplify
Browse files Browse the repository at this point in the history
  • Loading branch information
blgm committed May 17, 2020
1 parent cca64fb commit c2d9ae9
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 82 deletions.
43 changes: 14 additions & 29 deletions inspections.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package jsonry

import "reflect"
import (
"fmt"
"reflect"
)

func public(field reflect.StructField) bool {
return field.PkgPath == ""
Expand All @@ -18,35 +21,17 @@ func basicType(k reflect.Kind) bool {
}
}

type valueDetails struct {
value reflect.Value
typ reflect.Type
kind reflect.Kind
pointer bool
}
func checkForError(v reflect.Value) error {
if v.IsNil() {
return nil
}

func inspectValue(v reflect.Value) valueDetails {
k := v.Kind()
switch k {
case reflect.Ptr:
r := inspectValue(v.Elem())
r.pointer = true
return r
case reflect.Interface:
return inspectValue(v.Elem())
case reflect.Invalid:
return valueDetails{
value: v,
typ: nil,
kind: k,
pointer: false,
}
default:
return valueDetails{
value: v,
typ: v.Type(),
kind: k,
pointer: false,
if v.CanInterface() {
if err, ok := v.Interface().(error); ok {
return err
}
return fmt.Errorf("could not cast to error: %+v", v)
}
r := v.MethodByName("Error").Call(nil)
return fmt.Errorf("%s", r[0])
}
24 changes: 0 additions & 24 deletions interface_helpers.go

This file was deleted.

46 changes: 25 additions & 21 deletions marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,24 @@ import (
"code.cloudfoundry.org/jsonry/internal/path"
)

func Marshal(input interface{}) ([]byte, error) {
iv := inspectValue(reflect.ValueOf(input))
func Marshal(in interface{}) ([]byte, error) {
iv := reflect.Indirect(reflect.ValueOf(in))

if iv.kind != reflect.Struct {
return nil, fmt.Errorf(`the input must be a struct, not "%s"`, iv.kind)
if iv.Kind() != reflect.Struct {
return nil, fmt.Errorf(`the input must be a struct, not "%s"`, iv.Kind())
}

m, err := marshalStruct(context.Context{}, iv.value, iv.typ)
m, err := marshalStruct(context.Context{}, iv)
if err != nil {
return nil, err
}

return json.Marshal(m)
}

func marshalStruct(ctx context.Context, in reflect.Value, t reflect.Type) (map[string]interface{}, error) {
func marshalStruct(ctx context.Context, in reflect.Value) (map[string]interface{}, error) {
out := make(tree.Tree)
t := in.Type()

for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
Expand All @@ -49,23 +50,26 @@ func marshalStruct(ctx context.Context, in reflect.Value, t reflect.Type) (map[s
}

func marshal(ctx context.Context, in reflect.Value) (r interface{}, err error) {
input := inspectValue(in)
input := reflect.Indirect(in)
kind := input.Kind()

switch {
case implements(input.typ, (*json.Marshaler)(nil)):
r, err = marshalJSONMarshaler(ctx, input.value)
case basicType(input.kind):
r = in.Interface()
case input.kind == reflect.Invalid:
case kind == reflect.Invalid:
r = nil
case input.kind == reflect.Struct:
r, err = marshalStruct(ctx, input.value, input.typ)
case input.kind == reflect.Slice || input.kind == reflect.Array:
r, err = marshalList(ctx, input.value)
case input.kind == reflect.Map:
r, err = marshalMap(ctx, input.value)
case input.Type().Implements(reflect.TypeOf((*json.Marshaler)(nil)).Elem()):
r, err = marshalJSONMarshaler(ctx, input)
case kind == reflect.Interface:
r, err = marshal(ctx, input.Elem())
case basicType(kind):
r = in.Interface()
case kind == reflect.Struct:
r, err = marshalStruct(ctx, input)
case kind == reflect.Slice || kind == reflect.Array:
r, err = marshalList(ctx, input)
case kind == reflect.Map:
r, err = marshalMap(ctx, input)
default:
err = newUnsupportedTypeError(ctx, input.typ)
err = newUnsupportedTypeError(ctx, input.Type())
}

return
Expand Down Expand Up @@ -111,8 +115,8 @@ func marshalMap(ctx context.Context, in reflect.Value) (map[string]interface{},
func marshalJSONMarshaler(ctx context.Context, in reflect.Value) (interface{}, error) {
t := in.MethodByName("MarshalJSON").Call(nil)

if !t[1].IsNil() {
return nil, fmt.Errorf("error from MarshaJSON() call at %s: %w", ctx, toError(t[1]))
if err := checkForError(t[1]); err != nil {
return nil, fmt.Errorf("error from MarshaJSON() call at %s: %w", ctx, err)
}

var r interface{}
Expand Down
14 changes: 14 additions & 0 deletions marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,22 @@ var _ = Describe("Marshal", func() {

It("marshals a json.Marshaler", func() {
expectToMarshal(struct{ J implementsJSONMarshaler }{J: implementsJSONMarshaler{value: false}}, `{"J":"hello"}`)
expectToMarshal(struct{ J *implementsJSONMarshaler }{J: &implementsJSONMarshaler{value: false}}, `{"J":"hello"}`)
expectToFail(struct{ J implementsJSONMarshaler }{J: implementsJSONMarshaler{value: true}}, `error from MarshaJSON() call at field "J" (type "jsonry_test.implementsJSONMarshaler"): ouch`)
})

It("marshals from named types and type aliases", func() {
type alias = string
type named string
s := struct {
A alias
N named
}{
A: "foo",
N: named("bar"),
}
expectToMarshal(s, `{"A":"foo","N":"bar"}`)
})
})

Describe("recursive composition", func() {
Expand Down
18 changes: 10 additions & 8 deletions unmarhsal.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ import (
)

func Unmarshal(data []byte, receiver interface{}) error {
target := inspectValue(reflect.ValueOf(receiver))
target := reflect.ValueOf(receiver)

if !target.pointer {
if target.Kind() != reflect.Ptr {
return errors.New("receiver must be a pointer to a struct, got a non-pointer")
}
if target.kind != reflect.Struct {
return fmt.Errorf("receiver must be a pointer to a struct type, got: %s", target.typ)

target = target.Elem()
if target.Kind() != reflect.Struct {
return fmt.Errorf("receiver must be a pointer to a struct type, got: %s", target.Type())
}

var source map[string]interface{}
Expand All @@ -32,7 +34,7 @@ func Unmarshal(data []byte, receiver interface{}) error {
return fmt.Errorf("error parsing JSON: %w", err)
}

return unmarshalIntoStruct(context.Context{}, target.value, true, source)
return unmarshalIntoStruct(context.Context{}, target, true, source)
}

func unmarshalIntoStruct(ctx context.Context, target reflect.Value, found bool, source interface{}) error {
Expand Down Expand Up @@ -72,7 +74,7 @@ func unmarshal(ctx context.Context, target reflect.Value, found bool, source int

var err error
switch {
case implements(reflect.PtrTo(target.Type()), (*json.Unmarshaler)(nil)):
case reflect.PtrTo(target.Type()).Implements(reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()):
err = unmarshalIntoJSONUnmarshaler(ctx, target, found, source)
case basicType(kind), kind == reflect.Interface:
err = unmarshalInfoLeaf(ctx, target, found, source)
Expand Down Expand Up @@ -220,8 +222,8 @@ func unmarshalIntoJSONUnmarshaler(ctx context.Context, target reflect.Value, fou
elem := reflect.New(target.Type())
s := elem.MethodByName("UnmarshalJSON").Call([]reflect.Value{reflect.ValueOf(json)})

if !s[0].IsNil() {
return fmt.Errorf("error from UnmarshalJSON() call at %s: %w", ctx, toError(s[0]))
if err := checkForError(s[0]); err != nil {
return fmt.Errorf("error from UnmarshalJSON() call at %s: %w", ctx, err)
}

target.Set(elem.Elem())
Expand Down
15 changes: 15 additions & 0 deletions unmarshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,21 @@ var _ = Describe("Unmarshal", func() {

expectToFail(&s, `{"S":"fail"}`, `error from UnmarshalJSON() call at field "S" (type "jsonry_test.implementsJSONMarshaler"): ouch`)
})

It("unmarshals into named types and type aliases", func() {
type alias = string
type named string
var s struct {
A alias
N named
}
unmarshal(&s, `{"A":"foo","N":"bar"}`)
Expect(s.A).To(Equal("foo"))
Expect(s.N).To(Equal(named("bar")))

expectToFail(&s, `{"A":12}`, `cannot unmarshal "12" type "number" into field "A" (type "string")`)
expectToFail(&s, `{"N":13}`, `cannot unmarshal "13" type "number" into field "N" (type "jsonry_test.named")`)
})
})

Describe("recursive composition", func() {
Expand Down

0 comments on commit c2d9ae9

Please sign in to comment.