From 168e03357d3db6a5d72bfc6327dc2aa78727e2a2 Mon Sep 17 00:00:00 2001 From: Ggicci Date: Sat, 8 May 2021 11:50:24 +0800 Subject: [PATCH] feat: redesign Encoder interface and add tests --- decoders.go | 167 ++++++++++++++++++++++++++++------------- decoders_test.go | 191 +++++++++++++++++++++++++++++++++++++++++++++++ form.go | 8 +- 3 files changed, 311 insertions(+), 55 deletions(-) create mode 100644 decoders_test.go diff --git a/decoders.go b/decoders.go index 547064b..c93d07e 100644 --- a/decoders.go +++ b/decoders.go @@ -8,31 +8,31 @@ import ( ) type Decoder interface { - Decode([]byte, reflect.Value) error + Decode([]byte) (interface{}, error) } -type DecoderFunc func([]byte, reflect.Value) error +type DecoderFunc func([]byte) (interface{}, error) -func (fn DecoderFunc) Decode(data []byte, rv reflect.Value) error { - return fn(data, rv) +func (fn DecoderFunc) Decode(data []byte) (interface{}, error) { + return fn(data) } var builtinDecoders = map[reflect.Type]Decoder{ reflect.TypeOf(true): DecoderFunc(DecodeBool), reflect.TypeOf(int(0)): DecoderFunc(DecodeInt), - reflect.TypeOf(int8(0)): DecoderFunc(DecodeInt), - reflect.TypeOf(int16(0)): DecoderFunc(DecodeInt), - reflect.TypeOf(int32(0)): DecoderFunc(DecodeInt), - reflect.TypeOf(int64(0)): DecoderFunc(DecodeInt), + reflect.TypeOf(int8(0)): DecoderFunc(DecodeInt8), + reflect.TypeOf(int16(0)): DecoderFunc(DecodeInt16), + reflect.TypeOf(int32(0)): DecoderFunc(DecodeInt32), + reflect.TypeOf(int64(0)): DecoderFunc(DecodeInt64), reflect.TypeOf(uint(0)): DecoderFunc(DecodeUint), - reflect.TypeOf(uint8(0)): DecoderFunc(DecodeUint), - reflect.TypeOf(uint16(0)): DecoderFunc(DecodeUint), - reflect.TypeOf(uint32(0)): DecoderFunc(DecodeUint), - reflect.TypeOf(uint64(0)): DecoderFunc(DecodeUint), - reflect.TypeOf(float32(0.0)): DecoderFunc(DecodeFloat), - reflect.TypeOf(float64(0.0)): DecoderFunc(DecodeFloat), - reflect.TypeOf(complex64(0 + 1i)): DecoderFunc(DecodeComplex), - reflect.TypeOf(complex128(0 + 1i)): DecoderFunc(DecodeComplex), + reflect.TypeOf(uint8(0)): DecoderFunc(DecodeUint8), + reflect.TypeOf(uint16(0)): DecoderFunc(DecodeUint16), + reflect.TypeOf(uint32(0)): DecoderFunc(DecodeUint32), + reflect.TypeOf(uint64(0)): DecoderFunc(DecodeUint64), + reflect.TypeOf(float32(0.0)): DecoderFunc(DecodeFloat32), + reflect.TypeOf(float64(0.0)): DecoderFunc(DecodeFloat64), + reflect.TypeOf(complex64(0 + 1i)): DecoderFunc(DecodeComplex64), + reflect.TypeOf(complex128(0 + 1i)): DecoderFunc(DecodeComplex128), reflect.TypeOf(string("0")): DecoderFunc(DecodeString), reflect.TypeOf(time.Now()): DecoderFunc(DecodeTime), } @@ -47,57 +47,127 @@ func decoderOf(t reflect.Type) Decoder { return builtinDecoders[t] } -func DecodeBool(data []byte, rv reflect.Value) error { - v, err := strconv.ParseBool(string(data)) - if err != nil { - return err - } - rv.SetBool(v) - return nil +func DecodeBool(data []byte) (interface{}, error) { + return strconv.ParseBool(string(data)) } -func DecodeInt(data []byte, rv reflect.Value) error { +func DecodeInt(data []byte) (interface{}, error) { v, err := strconv.ParseInt(string(data), 10, 64) if err != nil { - return err + return nil, err } - rv.SetInt(v) - return nil + return int(v), nil } -func DecodeUint(data []byte, rv reflect.Value) error { +func DecodeInt8(data []byte) (interface{}, error) { + v, err := strconv.ParseInt(string(data), 10, 8) + if err != nil { + return nil, err + } + return int8(v), nil +} +func DecodeInt16(data []byte) (interface{}, error) { + v, err := strconv.ParseInt(string(data), 10, 16) + if err != nil { + return nil, err + } + return int16(v), nil +} +func DecodeInt32(data []byte) (interface{}, error) { + v, err := strconv.ParseInt(string(data), 10, 32) + if err != nil { + return nil, err + } + return int32(v), nil +} +func DecodeInt64(data []byte) (interface{}, error) { + v, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return nil, err + } + return int64(v), nil +} + +func DecodeUint(data []byte) (interface{}, error) { v, err := strconv.ParseUint(string(data), 10, 64) if err != nil { - return err + return nil, err } - rv.SetUint(v) - return nil + return uint(v), nil } -func DecodeFloat(data []byte, rv reflect.Value) error { +func DecodeUint8(data []byte) (interface{}, error) { + v, err := strconv.ParseUint(string(data), 10, 8) + if err != nil { + return nil, err + } + return uint8(v), nil +} +func DecodeUint16(data []byte) (interface{}, error) { + v, err := strconv.ParseUint(string(data), 10, 16) + if err != nil { + return nil, err + } + return uint16(v), nil +} +func DecodeUint32(data []byte) (interface{}, error) { + v, err := strconv.ParseUint(string(data), 10, 32) + if err != nil { + return nil, err + } + return uint32(v), nil +} +func DecodeUint64(data []byte) (interface{}, error) { + v, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return nil, err + } + return uint64(v), nil +} + +func DecodeFloat32(data []byte) (interface{}, error) { + v, err := strconv.ParseFloat(string(data), 32) + if err != nil { + return nil, err + } + return float32(v), nil +} + +func DecodeFloat64(data []byte) (interface{}, error) { v, err := strconv.ParseFloat(string(data), 64) if err != nil { - return err + return nil, err } - rv.SetFloat(v) - return nil + return float64(v), nil } -func DecodeComplex(data []byte, rv reflect.Value) error { +func DecodeComplex64(data []byte) (interface{}, error) { + v, err := strconv.ParseComplex(string(data), 64) + if err != nil { + return nil, err + } + return complex64(v), nil +} + +func DecodeComplex128(data []byte) (interface{}, error) { v, err := strconv.ParseComplex(string(data), 128) if err != nil { - return err + return nil, err } - rv.SetComplex(v) - return nil + return complex128(v), nil } -func DecodeString(data []byte, rv reflect.Value) error { - rv.SetString(string(data)) - return nil +func DecodeString(data []byte) (interface{}, error) { + return string(data), nil } -func parseTime(value string) (time.Time, error) { +// DecodeTime parses data bytes as time.Time in UTC timezone. +// Supported formats of the data bytes are: +// 1. RFC3339Nano string, e.g. "2006-01-02T15:04:05-07:00" +// 2. Unix timestamp, e.g. "1136239445" +func DecodeTime(data []byte) (interface{}, error) { + value := string(data) + // Try parsing value as RFC3339 format. if t, err := time.Parse(time.RFC3339Nano, value); err == nil { return t.UTC(), nil @@ -109,14 +179,5 @@ func parseTime(value string) (time.Time, error) { return time.Unix(timestamp, 0).UTC(), nil } - return time.Time{}, fmt.Errorf("invalid time value, use time.RFC3339Nano format or timestamp") -} - -func DecodeTime(data []byte, rv reflect.Value) error { - timeValue, err := parseTime(string(data)) - if err != nil { - return err - } - rv.Set(reflect.ValueOf(timeValue)) - return nil + return time.Time{}, fmt.Errorf("invalid time value") } diff --git a/decoders_test.go b/decoders_test.go new file mode 100644 index 0000000..e6d0c71 --- /dev/null +++ b/decoders_test.go @@ -0,0 +1,191 @@ +package httpin + +import ( + "testing" + "time" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestBuiltinDecoders(t *testing.T) { + Convey("Decoder for bool type", t, func() { + v, err := DecodeBool([]byte("true")) + So(v, ShouldBeTrue) + So(v, ShouldHaveSameTypeAs, true) + So(err, ShouldBeNil) + v, err = DecodeBool([]byte("false")) + So(v, ShouldBeFalse) + So(err, ShouldBeNil) + v, err = DecodeBool([]byte("1")) + So(v, ShouldBeTrue) + So(err, ShouldBeNil) + v, err = DecodeBool([]byte("apple")) + So(err, ShouldBeError) + }) + + Convey("Decoder for int type", t, func() { + v, err := DecodeInt([]byte("2045")) + So(v, ShouldEqual, 2045) + So(v, ShouldHaveSameTypeAs, int(1)) + So(err, ShouldBeNil) + v, err = DecodeInt([]byte("apple")) + So(err, ShouldBeError) + }) + + Convey("Decoder for int8 type", t, func() { + v, err := DecodeInt8([]byte("127")) + So(v, ShouldEqual, 127) + So(v, ShouldHaveSameTypeAs, int8(1)) + So(err, ShouldBeNil) + v, err = DecodeInt8([]byte("128")) + So(err, ShouldBeError) + v, err = DecodeInt8([]byte("apple")) + So(err, ShouldBeError) + }) + + Convey("Decoder for int16 type", t, func() { + v, err := DecodeInt16([]byte("32767")) + So(v, ShouldEqual, 32767) + So(v, ShouldHaveSameTypeAs, int16(1)) + So(err, ShouldBeNil) + v, err = DecodeInt16([]byte("32768")) + So(err, ShouldBeError) + v, err = DecodeInt16([]byte("apple")) + So(err, ShouldBeError) + }) + + Convey("Decoder for int32 type", t, func() { + v, err := DecodeInt32([]byte("2147483647")) + So(v, ShouldEqual, 2147483647) + So(v, ShouldHaveSameTypeAs, int32(1)) + So(err, ShouldBeNil) + v, err = DecodeInt32([]byte("2147483648")) + So(err, ShouldBeError) + v, err = DecodeInt32([]byte("apple")) + So(err, ShouldBeError) + }) + + Convey("Decoder for int64 type", t, func() { + v, err := DecodeInt64([]byte("9223372036854775807")) + So(v, ShouldEqual, 9223372036854775807) + So(v, ShouldHaveSameTypeAs, int64(1)) + So(err, ShouldBeNil) + v, err = DecodeInt64([]byte("9223372036854775808")) + So(err, ShouldBeError) + v, err = DecodeInt64([]byte("apple")) + So(err, ShouldBeError) + }) + + Convey("Decoder for uint type", t, func() { + v, err := DecodeUint([]byte("2045")) + So(v, ShouldEqual, uint(2045)) + So(v, ShouldHaveSameTypeAs, uint(1)) + So(err, ShouldBeNil) + v, err = DecodeUint([]byte("apple")) + So(err, ShouldBeError) + }) + + Convey("Decoder for uint8 type", t, func() { + v, err := DecodeUint8([]byte("255")) + So(v, ShouldEqual, uint8(255)) + So(v, ShouldHaveSameTypeAs, uint8(1)) + So(err, ShouldBeNil) + v, err = DecodeUint8([]byte("256")) + So(err, ShouldBeError) + v, err = DecodeUint8([]byte("apple")) + So(err, ShouldBeError) + }) + + Convey("Decoder for uint16 type", t, func() { + v, err := DecodeUint16([]byte("65535")) + So(v, ShouldEqual, uint16(65535)) + So(v, ShouldHaveSameTypeAs, uint16(1)) + So(err, ShouldBeNil) + v, err = DecodeUint16([]byte("65536")) + So(err, ShouldBeError) + v, err = DecodeUint16([]byte("apple")) + So(err, ShouldBeError) + }) + + Convey("Decoder for uint32 type", t, func() { + v, err := DecodeUint32([]byte("4294967295")) + So(v, ShouldEqual, uint32(4294967295)) + So(v, ShouldHaveSameTypeAs, uint32(1)) + So(err, ShouldBeNil) + v, err = DecodeUint32([]byte("4294967296")) + So(err, ShouldBeError) + v, err = DecodeUint32([]byte("apple")) + So(err, ShouldBeError) + }) + + Convey("Decoder for uint64 type", t, func() { + v, err := DecodeUint64([]byte("18446744073709551615")) + So(v, ShouldEqual, uint64(18446744073709551615)) + So(v, ShouldHaveSameTypeAs, uint64(1)) + So(err, ShouldBeNil) + v, err = DecodeUint64([]byte("18446744073709551616")) + So(err, ShouldBeError) + v, err = DecodeUint64([]byte("apple")) + So(err, ShouldBeError) + }) + + Convey("Decoder for float32 type", t, func() { + v, err := DecodeFloat32([]byte("3.1415926")) + So(v, ShouldEqual, 3.1415926) + So(v, ShouldHaveSameTypeAs, float32(0.0)) + So(err, ShouldBeNil) + v, err = DecodeFloat32([]byte("apple")) + So(err, ShouldBeError) + }) + + Convey("Decoder for float64 type", t, func() { + v, err := DecodeFloat64([]byte("3.1415926")) + So(v, ShouldEqual, 3.1415926) + So(v, ShouldHaveSameTypeAs, float64(0.0)) + So(err, ShouldBeNil) + v, err = DecodeFloat64([]byte("apple")) + So(err, ShouldBeError) + }) + + Convey("Decoder for complex64 type", t, func() { + v, err := DecodeComplex64([]byte("1+4i")) + So(v, ShouldEqual, 1+4i) + So(v, ShouldHaveSameTypeAs, complex64(1+4i)) + So(err, ShouldBeNil) + v, err = DecodeComplex64([]byte("apple")) + So(err, ShouldBeError) + }) + + Convey("Decoder for complex128 type", t, func() { + v, err := DecodeComplex128([]byte("1+4i")) + So(v, ShouldEqual, 1+4i) + So(v, ShouldHaveSameTypeAs, complex128(1+4i)) + So(err, ShouldBeNil) + v, err = DecodeComplex128([]byte("apple")) + So(err, ShouldBeError) + }) + + Convey("Decoder for string type", t, func() { + v, err := DecodeString([]byte("hello")) + So(v, ShouldEqual, "hello") + So(v, ShouldHaveSameTypeAs, string("")) + So(err, ShouldBeNil) + }) + + Convey("Decoder for time.Time type", t, func() { + v, err := DecodeTime([]byte("1991-11-10T08:00:00+08:00")) + So(v, ShouldEqual, time.Date(1991, 11, 10, 8, 0, 0, 0, time.FixedZone("Asia/Shanghai", +8*3600))) + So(v, ShouldHaveSameTypeAs, time.Time{}) + So(v.(time.Time).Location(), ShouldEqual, time.UTC) + So(err, ShouldBeNil) + + v, err = DecodeTime([]byte("678088800")) + So(v, ShouldEqual, time.Date(1991, 6, 28, 6, 0, 0, 0, time.UTC)) + So(v, ShouldHaveSameTypeAs, time.Time{}) + So(v.(time.Time).Location(), ShouldEqual, time.UTC) + So(err, ShouldBeNil) + + v, err = DecodeTime([]byte("apple")) + So(err, ShouldBeError) + }) +} diff --git a/form.go b/form.go index b331a90..25c5d16 100644 --- a/form.go +++ b/form.go @@ -56,8 +56,10 @@ func extractFromKVSWithKey(ctx *DirectiveContext, kvs map[string][]string, key s if len(formValues) > 0 { got = formValues[0] } - if err := decoder.Decode([]byte(got), ctx.Value.Elem()); err != nil { + if interfaceValue, err := decoder.Decode([]byte(got)); err != nil { return err + } else { + ctx.Value.Elem().Set(reflect.ValueOf(interfaceValue)) } ctx.DeliverContextValue(fieldSet, true) @@ -80,8 +82,10 @@ func extractFromKVSWithKeyForSlice(ctx *DirectiveContext, kvs map[string][]strin theSlice := reflect.MakeSlice(ctx.ValueType, len(formValues), len(formValues)) for i, formValue := range formValues { - if err := decoder.Decode([]byte(formValue), theSlice.Index(i)); err != nil { + if interfaceValue, err := decoder.Decode([]byte(formValue)); err != nil { return fmt.Errorf("at index %d: %w", i, err) + } else { + theSlice.Index(i).Set(reflect.ValueOf(interfaceValue)) } }