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:
parent
0201a0615e
commit
168e03357d
167
decoders.go
167
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")
|
||||
}
|
||||
|
191
decoders_test.go
Normal file
191
decoders_test.go
Normal 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)
|
||||
})
|
||||
}
|
8
form.go
8
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user