Skip to content

Commit

Permalink
Marshalling: validation of type
Browse files Browse the repository at this point in the history
  • Loading branch information
blgm committed May 4, 2020
1 parent 91f2cae commit 338f7c5
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 10 deletions.
18 changes: 18 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package jsonry

import (
"fmt"
"reflect"
)

type UnsupportedType struct {
TypeName string
}

func NewUnsupportedTypeError(t reflect.Type) *UnsupportedType {
return &UnsupportedType{TypeName: t.String()}
}

func (u *UnsupportedType) Error() string {
return fmt.Sprintf("unsupported type: %s", u.TypeName)
}
41 changes: 38 additions & 3 deletions marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@ import (
"reflect"
)

type metakind uint

const (
metakindBasic metakind = iota
metakindUnsupported
)

func Marshal(input interface{}) ([]byte, error) {
i := reflect.ValueOf(input)

if i.Kind()==reflect.Ptr {
if i.Kind() == reflect.Ptr {
i = i.Elem()
}

Expand All @@ -29,10 +36,38 @@ func marshal(in reflect.Value) (map[string]interface{}, error) {
out := make(map[string]interface{})

t := in.Type()
for i := 0; i<t.NumField(); i++ {
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
out[f.Name] = in.Field(i).Interface()
switch actual(in.Field(i)) {
case metakindBasic:
out[f.Name] = in.Field(i).Interface()
default:
return nil, NewUnsupportedTypeError(actualType(in.Field(i)))
}
}

return out, nil
}

func actual(v reflect.Value) metakind {
switch v.Kind() {
case reflect.Interface:
return actual(v.Elem())
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 metakindBasic
default:
return metakindUnsupported
}
}

func actualType(v reflect.Value) reflect.Type {
switch v.Kind() {
case reflect.Interface:
return v.Elem().Type()
default:
return v.Type()
}
}
39 changes: 32 additions & 7 deletions marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,42 @@ package jsonry_test
import (
"code.cloudfoundry.org/jsonry"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"
)

var _ = Describe("Marshal", func() {
Describe("basic types", func() {
It("basic", func() {
type t struct{A string}
out, err := jsonry.Marshal(t{A:"hello"})
Expect(err).NotTo(HaveOccurred())
Expect(out).To(MatchJSON(`{"A":"hello"}`))
})
Describe("supported types", func() {
type c struct{ V interface{} }
var i int

DescribeTable(
"supported types",
func(input c, expected string) {
out, err := jsonry.Marshal(input)
Expect(err).NotTo(HaveOccurred())
Expect(out).To(MatchJSON(expected))
},
Entry("string", c{V: "hello"}, `{"V":"hello"}`),
Entry("boolean", c{V: true}, `{"V":true}`),
Entry("int", c{V: 42}, `{"V":42}`),
Entry("float", c{V: 4.2}, `{"V":4.2}`),
)

DescribeTable(
"unsupported types",
func(input c, typeName string) {
_, err := jsonry.Marshal(input)
Expect(err).To(MatchError(ContainSubstring("unsupported type: %s", typeName)))
},
Entry("complex", c{V: complex(1, 2)}, "complex"),
Entry("slice", c{V: []string{"hello"}}, "[]string"),
Entry("array", c{V: [1]string{"hello"}}, "[1]string"),
Entry("map", c{V: make(map[string]interface{})}, "map[string]interface {}"),
Entry("channel", c{V: make(chan bool)}, "chan bool"),
Entry("func", c{V: func() {}}, "func()"),
Entry("pointer", c{V: &i}, "*int"),
)
})

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

0 comments on commit 338f7c5

Please sign in to comment.