Skip to content

Commit

Permalink
Marshal can delegate to MarshalJSONry()
Browse files Browse the repository at this point in the history
  • Loading branch information
blgm committed May 7, 2020
1 parent 8c5a726 commit ad65395
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 23 deletions.
45 changes: 24 additions & 21 deletions marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ func Marshal(input interface{}) ([]byte, error) {
}

func marshal(ctx context.Context, in reflect.Value) (r interface{}, err error) {
ut := underlyingType(in)
uv := underlyingValue(in)
ut := uv.Type()
k := uv.Kind()

switch {
case ut.Implements(reflect.TypeOf((*json.Marshaler)(nil)).Elem()):
r, err = marshalJSONMarshaler(ctx, uv)
case ut.Implements(reflect.TypeOf((*Marshaler)(nil)).Elem()):
panic("not done yet")
r, err = marshalJSONryMarshaler(ctx, uv)
case k == reflect.Struct:
r, err = marshalStruct(ctx, uv)
case isBasicType(k):
Expand Down Expand Up @@ -97,7 +97,7 @@ func marshalMap(ctx context.Context, in reflect.Value) (map[string]interface{},
for iter.Next() {
k := iter.Key()
if k.Kind() != reflect.String {
return nil, NewUnsupportedKeyTypeError(ctx, underlyingType(in))
return nil, NewUnsupportedKeyTypeError(ctx, in.Type())
}

ctx := ctx.WithKey(k.String(), k.Type())
Expand Down Expand Up @@ -128,27 +128,18 @@ func marshalJSONMarshaler(ctx context.Context, in reflect.Value) (interface{}, e
return r, nil
}

func isBasicType(k reflect.Kind) bool {
switch k {
case reflect.String, reflect.Bool,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64:
return true
default:
return false
func marshalJSONryMarshaler(ctx context.Context, in reflect.Value) (interface{}, error) {
t := in.MethodByName("MarshalJSONry").Call(nil)

if !t[1].IsNil() {
return nil, fmt.Errorf("error from MarshaJSONry() call %s: %w", ctx, toError(t[1]))
}
}

func underlyingType(v reflect.Value) reflect.Type {
switch v.Kind() {
case reflect.Interface:
return underlyingType(v.Elem())
case reflect.Ptr:
return reflect.PtrTo(underlyingType(v.Elem()))
default:
return v.Type()
if !t[0].CanInterface() {
return nil, fmt.Errorf(`cannot convert output of MarshaJSONry() to interface "%s" %s`, t[0], ctx)
}

return t[0].Interface(), nil
}

func underlyingValue(v reflect.Value) reflect.Value {
Expand All @@ -160,6 +151,18 @@ func underlyingValue(v reflect.Value) reflect.Value {
}
}

func isBasicType(k reflect.Kind) bool {
switch k {
case reflect.String, reflect.Bool,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64:
return true
default:
return false
}
}

func toError(v reflect.Value) error {
if v.CanInterface() {
if err, ok := v.Interface().(error); ok {
Expand Down
6 changes: 4 additions & 2 deletions marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ func (j jsm) MarshalJSON() ([]byte, error) {
return json.Marshal("hello")
}

type jrm bool
type jrm struct{ value bool }

func (j jrm) MarshalJSONry() (interface{}, error) {
if j {
if j.value {
return nil, errors.New("ouch")
}
return "hello", nil
Expand Down Expand Up @@ -67,6 +67,7 @@ var _ = Describe("Marshal", func() {
Entry("map of interfaces", c{V: map[string]interface{}{"foo": "hello", "bar": true, "baz": 42}}, `{"V":{"foo":"hello","bar":true,"baz":42}}`),
Entry("map of strings", c{V: map[string]string{"foo": "hello", "bar": "true", "baz": "42"}}, `{"V":{"foo":"hello","bar":"true","baz":"42"}}`),
Entry("json.Marshaler", c{V: jsm{}}, `{"V": "hello"}`),
Entry("jsonry.Marshaler", c{V: jrm{}}, `{"V": "hello"}`),
)

DescribeTable(
Expand All @@ -85,6 +86,7 @@ var _ = Describe("Marshal", func() {
Entry("func", c{V: func() {}}, `unsupported type "func()" at field "V" (type "interface {}")`),
Entry("map with non-string keys", c{V: map[int]interface{}{4: 3}}, `maps must only have strings keys for "map[int]interface {}" at field "V" (type "interface {}")`),
Entry("json.Marshaler with failure", c{V: jsm{value: true}}, `error from MarshaJSON() call at field "V" (type "interface {}"): ouch`),
Entry("jsonry.Marshaler with failure", c{V: jrm{value: true}}, `error from MarshaJSONry() call at field "V" (type "interface {}"): ouch`),
)

Describe("inputs", func() {
Expand Down

0 comments on commit ad65395

Please sign in to comment.