1
0
mirror of https://github.com/ggicci/httpin.git synced 2024-11-24 08:32:45 +02:00

feat: redesign Encoder interface and add tests

This commit is contained in:
Ggicci 2021-05-08 11:50:24 +08:00
parent 0201a0615e
commit 168e03357d
3 changed files with 311 additions and 55 deletions

View File

@ -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")
}

191
decoders_test.go Normal file
View File

@ -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)
})
}

View File

@ -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))
}
}