Skip to content

Commit

Permalink
Unmashal lists
Browse files Browse the repository at this point in the history
  • Loading branch information
blgm committed May 11, 2020
1 parent 19446c0 commit 19fcedc
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 97 deletions.
32 changes: 28 additions & 4 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,53 @@ type UnsupportedType struct {
typ reflect.Type
}

func newUnsupportedTypeError(ctx context.Context, t reflect.Type) *UnsupportedType {
func newUnsupportedTypeError(ctx context.Context, t reflect.Type) error {
return &UnsupportedType{
context: ctx,
typ: t,
}
}

func (u UnsupportedType) Error() string {
return fmt.Sprintf(`unsupported type "%s" %s`, u.typ, u.context)
return fmt.Sprintf(`unsupported type "%s" at %s`, u.typ, u.context)
}

type UnsupportedKeyType struct {
context context.Context
typ reflect.Type
}

func newUnsupportedKeyTypeError(ctx context.Context, t reflect.Type) *UnsupportedKeyType {
func newUnsupportedKeyTypeError(ctx context.Context, t reflect.Type) error {
return &UnsupportedKeyType{
context: ctx,
typ: t,
}
}

func (u UnsupportedKeyType) Error() string {
return fmt.Sprintf(`maps must only have strings keys for "%s" %s`, u.typ, u.context)
return fmt.Sprintf(`maps must only have strings keys for "%s" at %s`, u.typ, u.context)
}

type ConversionError struct {
context context.Context
typ reflect.Type
value interface{}
}

func newConversionError(ctx context.Context, v interface{}, t reflect.Type) error {
return &ConversionError{
context: ctx,
typ: t,
value: v,
}
}

func (c ConversionError) Error() string {
msg := fmt.Sprintf(`cannot unmarshal "%+v" `, c.value)

if c.typ != nil {
msg = fmt.Sprintf(`%stype "%s" `, msg, c.typ)
}

return msg + "into " + c.context.String()
}
24 changes: 12 additions & 12 deletions inspections.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ func basicType(k reflect.Kind) bool {
}

type valueDetails struct {
realValue reflect.Value
realType reflect.Type
realKind reflect.Kind
pointer bool
value reflect.Value
typ reflect.Type
kind reflect.Kind
pointer bool
}

func inspectValue(v reflect.Value) valueDetails {
Expand All @@ -36,17 +36,17 @@ func inspectValue(v reflect.Value) valueDetails {
return inspectValue(v.Elem())
case reflect.Invalid:
return valueDetails{
realValue: v,
realType: nil,
realKind: k,
pointer: false,
value: v,
typ: nil,
kind: k,
pointer: false,
}
default:
return valueDetails{
realValue: v,
realType: v.Type(),
realKind: k,
pointer: false,
value: v,
typ: v.Type(),
kind: k,
pointer: false,
}
}
}
6 changes: 3 additions & 3 deletions internal/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ func (ctx Context) WithKey(k string, t reflect.Type) Context {
func (ctx Context) String() string {
switch len(ctx) {
case 0:
return "at the root"
return "root path"
case 1:
return fmt.Sprintf("at %s", ctx.leaf())
return ctx.leaf().String()
default:
return fmt.Sprintf("at %s path %s", ctx.leaf(), ctx.path())
return fmt.Sprintf("%s path %s", ctx.leaf(), ctx.path())
}
}

Expand Down
18 changes: 9 additions & 9 deletions internal/context/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,29 @@ var _ = Describe("Context", func() {
When("it is the zero value", func() {
It("reports being at the root", func() {
ctx := context.Context{}
Expect(ctx.String()).To(Equal("at the root"))
Expect(ctx.String()).To(Equal("root path"))
})
})

When("it has a field", func() {
It("reports the field detail", func() {
ctx := context.Context{}.WithField("Foo", reflect.TypeOf(""))
Expect(ctx.String()).To(Equal(`at field "Foo" (type "string")`))
Expect(ctx.String()).To(Equal(`field "Foo" (type "string")`))
})
})

When("it has an index", func() {
It("reports the index detail", func() {
ctx := context.Context{}.WithIndex(4, reflect.TypeOf(true))
Expect(ctx.String()).To(Equal(`at index 4 (type "bool")`))
Expect(ctx.String()).To(Equal(`index 4 (type "bool")`))

})
})

When("it has an key", func() {
It("reports the key detail", func() {
ctx := context.Context{}.WithKey("foo", reflect.TypeOf(true))
Expect(ctx.String()).To(Equal(`at key "foo" (type "bool")`))
Expect(ctx.String()).To(Equal(`key "foo" (type "bool")`))

})
})
Expand All @@ -46,7 +46,7 @@ var _ = Describe("Context", func() {
WithField("Bar", reflect.TypeOf(true)).
WithField("Baz", reflect.TypeOf(42))

Expect(ctx.String()).To(Equal(`at field "Baz" (type "int") path Foo.Bar.Baz`))
Expect(ctx.String()).To(Equal(`field "Baz" (type "int") path Foo.Bar.Baz`))
})
})

Expand All @@ -56,7 +56,7 @@ var _ = Describe("Context", func() {
WithIndex(3, reflect.TypeOf("")).
WithIndex(5, reflect.TypeOf(true)).
WithIndex(4, reflect.TypeOf(42))
Expect(ctx.String()).To(Equal(`at index 4 (type "int") path [3][5][4]`))
Expect(ctx.String()).To(Equal(`index 4 (type "int") path [3][5][4]`))
})
})

Expand All @@ -66,7 +66,7 @@ var _ = Describe("Context", func() {
WithKey("foo", reflect.TypeOf(true)).
WithKey("bar", reflect.TypeOf(42)).
WithKey("baz", reflect.TypeOf(""))
Expect(ctx.String()).To(Equal(`at key "baz" (type "string") path ["foo"]["bar"]["baz"]`))
Expect(ctx.String()).To(Equal(`key "baz" (type "string") path ["foo"]["bar"]["baz"]`))

})
})
Expand All @@ -82,7 +82,7 @@ var _ = Describe("Context", func() {
WithField("Baz", reflect.TypeOf(42)).
WithKey("bar", reflect.TypeOf(42)).
WithIndex(4, reflect.TypeOf(42))
Expect(ctx.String()).To(Equal(`at index 4 (type "int") path [3].Foo["foo"][5].Bar.Baz["bar"][4]`))
Expect(ctx.String()).To(Equal(`index 4 (type "int") path [3].Foo["foo"][5].Bar.Baz["bar"][4]`))

ctx = context.Context{}.
WithField("Foo", reflect.TypeOf("")).
Expand All @@ -93,7 +93,7 @@ var _ = Describe("Context", func() {
WithKey("bar", reflect.TypeOf(42)).
WithIndex(4, reflect.TypeOf(42)).
WithField("Baz", reflect.TypeOf(42))
Expect(ctx.String()).To(Equal(`at field "Baz" (type "int") path Foo[5][3]["foo"].Bar["bar"][4].Baz`))
Expect(ctx.String()).To(Equal(`field "Baz" (type "int") path Foo[5][3]["foo"].Bar["bar"][4].Baz`))
})
})

Expand Down
38 changes: 19 additions & 19 deletions marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ type Marshaler interface {
func Marshal(input interface{}) ([]byte, error) {
iv := inspectValue(reflect.ValueOf(input))

if iv.realKind != reflect.Struct {
return nil, fmt.Errorf(`the input must be a struct, not "%s"`, iv.realKind)
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.realValue, iv.realType)
m, err := marshalStruct(context.Context{}, iv.value, iv.typ)
if err != nil {
return nil, err
}
Expand All @@ -36,22 +36,22 @@ func marshal(ctx context.Context, in reflect.Value) (r interface{}, err error) {
input := inspectValue(in)

switch {
case implements(input.realType, (*json.Marshaler)(nil)):
r, err = marshalJSONMarshaler(ctx, input.realValue)
case implements(input.realType, (*Marshaler)(nil)):
r, err = marshalJSONryMarshaler(ctx, input.realValue)
case basicType(input.realKind):
case implements(input.typ, (*json.Marshaler)(nil)):
r, err = marshalJSONMarshaler(ctx, input.value)
case implements(input.typ, (*Marshaler)(nil)):
r, err = marshalJSONryMarshaler(ctx, input.value)
case basicType(input.kind):
r = in.Interface()
case input.realKind == reflect.Invalid:
case input.kind == reflect.Invalid:
r = nil
case input.realKind == reflect.Struct:
r, err = marshalStruct(ctx, input.realValue, input.realType)
case input.realKind == reflect.Slice || input.realKind == reflect.Array:
r, err = marshalList(ctx, input.realValue)
case input.realKind == reflect.Map:
r, err = marshalMap(ctx, input.realValue)
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)
default:
err = newUnsupportedTypeError(ctx, input.realType)
err = newUnsupportedTypeError(ctx, input.typ)
}

return
Expand Down Expand Up @@ -120,13 +120,13 @@ func marshalJSONMarshaler(ctx context.Context, in reflect.Value) (interface{}, e
t := in.MethodByName("MarshalJSON").Call(nil)

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

var r interface{}
err := json.Unmarshal(t[0].Bytes(), &r)
if err != nil {
return nil, fmt.Errorf(`error parsing MarshaJSON() output "%s" %s: %w`, t[0].Bytes(), ctx, err)
return nil, fmt.Errorf(`error parsing MarshaJSON() output "%s" at %s: %w`, t[0].Bytes(), ctx, err)
}

return r, nil
Expand All @@ -136,7 +136,7 @@ func marshalJSONryMarshaler(ctx context.Context, in reflect.Value) (interface{},
t := in.MethodByName("MarshalJSONry").Call(nil)

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

return marshal(ctx, t[0])
Expand Down

0 comments on commit 19fcedc

Please sign in to comment.