diff --git a/go.sum b/go.sum index b46929e..cddc630 100644 --- a/go.sum +++ b/go.sum @@ -25,13 +25,11 @@ github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M= -github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.15.0 h1:1V1NfVQR87RtWAgp1lv9JZJ5Jap+XFGKPi00andXGi4= github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.4 h1:NiTx7EEvBzu9sFOD1zORteLSt3o8gnlvZZwSE9TnY9U= -github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ= +github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ= github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -62,12 +60,11 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -77,6 +74,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/internal/hscan/hscan.go b/internal/hscan/hscan.go index 181260b..6f97f92 100644 --- a/internal/hscan/hscan.go +++ b/internal/hscan/hscan.go @@ -15,17 +15,17 @@ var ( decoders = []decoderFunc{ reflect.Bool: decodeBool, reflect.Int: decodeInt, - reflect.Int8: decodeInt, - reflect.Int16: decodeInt, - reflect.Int32: decodeInt, - reflect.Int64: decodeInt, + reflect.Int8: decodeInt8, + reflect.Int16: decodeInt16, + reflect.Int32: decodeInt32, + reflect.Int64: decodeInt64, reflect.Uint: decodeUint, - reflect.Uint8: decodeUint, - reflect.Uint16: decodeUint, - reflect.Uint32: decodeUint, - reflect.Uint64: decodeUint, - reflect.Float32: decodeFloat, - reflect.Float64: decodeFloat, + reflect.Uint8: decodeUint8, + reflect.Uint16: decodeUint16, + reflect.Uint32: decodeUint32, + reflect.Uint64: decodeUint64, + reflect.Float32: decodeFloat32, + reflect.Float64: decodeFloat64, reflect.Complex64: decodeUnsupported, reflect.Complex128: decodeUnsupported, reflect.Array: decodeUnsupported, @@ -106,8 +106,28 @@ func decodeBool(f reflect.Value, s string) error { return nil } +func decodeInt8(f reflect.Value, s string) error { + return decodeNumber(f, s, 8) +} + +func decodeInt16(f reflect.Value, s string) error { + return decodeNumber(f, s, 16) +} + +func decodeInt32(f reflect.Value, s string) error { + return decodeNumber(f, s, 32) +} + +func decodeInt64(f reflect.Value, s string) error { + return decodeNumber(f, s, 64) +} + func decodeInt(f reflect.Value, s string) error { - v, err := strconv.ParseInt(s, 10, 0) + return decodeNumber(f, s, 0) +} + +func decodeNumber(f reflect.Value, s string, bitSize int) error { + v, err := strconv.ParseInt(s, 10, bitSize) if err != nil { return err } @@ -115,8 +135,28 @@ func decodeInt(f reflect.Value, s string) error { return nil } +func decodeUint8(f reflect.Value, s string) error { + return decodeUnsignedNumber(f, s, 8) +} + +func decodeUint16(f reflect.Value, s string) error { + return decodeUnsignedNumber(f, s, 16) +} + +func decodeUint32(f reflect.Value, s string) error { + return decodeUnsignedNumber(f, s, 32) +} + +func decodeUint64(f reflect.Value, s string) error { + return decodeUnsignedNumber(f, s, 64) +} + func decodeUint(f reflect.Value, s string) error { - v, err := strconv.ParseUint(s, 10, 0) + return decodeUnsignedNumber(f, s, 0) +} + +func decodeUnsignedNumber(f reflect.Value, s string, bitSize int) error { + v, err := strconv.ParseUint(s, 10, bitSize) if err != nil { return err } @@ -124,8 +164,18 @@ func decodeUint(f reflect.Value, s string) error { return nil } -func decodeFloat(f reflect.Value, s string) error { - v, err := strconv.ParseFloat(s, 0) +func decodeFloat32(f reflect.Value, s string) error { + v, err := strconv.ParseFloat(s, 32) + if err != nil { + return err + } + f.SetFloat(v) + return nil +} + +// although the default is float64, but we better define it. +func decodeFloat64(f reflect.Value, s string) error { + v, err := strconv.ParseFloat(s, 64) if err != nil { return err } diff --git a/internal/hscan/hscan_test.go b/internal/hscan/hscan_test.go index f7a88f0..1f78bc7 100644 --- a/internal/hscan/hscan_test.go +++ b/internal/hscan/hscan_test.go @@ -1,6 +1,8 @@ package hscan import ( + "math" + "strconv" "testing" . "github.com/onsi/ginkgo" @@ -11,12 +13,21 @@ type data struct { Omit string `redis:"-"` Empty string - String string `redis:"string"` - Bytes []byte `redis:"byte"` - Int int `redis:"int"` - Uint uint `redis:"uint"` - Float float32 `redis:"float"` - Bool bool `redis:"bool"` + String string `redis:"string"` + Bytes []byte `redis:"byte"` + Int int `redis:"int"` + Int8 int8 `redis:"int8"` + Int16 int16 `redis:"int16"` + Int32 int32 `redis:"int32"` + Int64 int64 `redis:"int64"` + Uint uint `redis:"uint"` + Uint8 uint8 `redis:"uint8"` + Uint16 uint16 `redis:"uint16"` + Uint32 uint32 `redis:"uint32"` + Uint64 uint64 `redis:"uint64"` + Float float32 `redis:"float"` + Float64 float64 `redis:"float64"` + Bool bool `redis:"bool"` } type i []interface{} @@ -43,6 +54,47 @@ var _ = Describe("Scan", func() { Expect(Scan(data{}, i{"key", "string"}, i{nil, nil})).To(HaveOccurred()) }) + It("number out of range", func() { + f := func(v uint64) string { + return strconv.FormatUint(v, 10) + "1" + } + keys := i{"int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "float64"} + vals := i{ + f(math.MaxInt8), f(math.MaxInt16), f(math.MaxInt32), f(math.MaxInt64), + f(math.MaxUint8), f(math.MaxUint16), f(math.MaxUint32), strconv.FormatUint(math.MaxUint64, 10) + "1", + "13.4028234663852886e+38", "11.79769313486231570e+308", + } + for k, v := range keys { + var d data + Expect(Scan(&d, i{v}, i{vals[k]})).To(HaveOccurred()) + } + + //success + f = func(v uint64) string { + return strconv.FormatUint(v, 10) + } + keys = i{"int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "float64"} + vals = i{ + f(math.MaxInt8), f(math.MaxInt16), f(math.MaxInt32), f(math.MaxInt64), + f(math.MaxUint8), f(math.MaxUint16), f(math.MaxUint32), strconv.FormatUint(math.MaxUint64, 10), + "3.40282346638528859811704183484516925440e+38", "1.797693134862315708145274237317043567981e+308", + } + var d data + Expect(Scan(&d, keys, vals)).NotTo(HaveOccurred()) + Expect(d).To(Equal(data{ + Int8: math.MaxInt8, + Int16: math.MaxInt16, + Int32: math.MaxInt32, + Int64: math.MaxInt64, + Uint8: math.MaxUint8, + Uint16: math.MaxUint16, + Uint32: math.MaxUint32, + Uint64: math.MaxUint64, + Float: math.MaxFloat32, + Float64: math.MaxFloat64, + })) + }) + It("scans good values", func() { var d data @@ -50,16 +102,22 @@ var _ = Describe("Scan", func() { Expect(Scan(&d, i{"key"}, i{"value"})).NotTo(HaveOccurred()) Expect(d).To(Equal(data{})) - keys := i{"string", "byte", "int", "uint", "float", "bool"} - vals := i{"str!", "bytes!", "123", "456", "123.456", "1"} + keys := i{"string", "byte", "int", "int64", "uint", "uint64", "float", "float64", "bool"} + vals := i{ + "str!", "bytes!", "123", "123456789123456789", "456", "987654321987654321", + "123.456", "123456789123456789.987654321987654321", "1", + } Expect(Scan(&d, keys, vals)).NotTo(HaveOccurred()) Expect(d).To(Equal(data{ - String: "str!", - Bytes: []byte("bytes!"), - Int: 123, - Uint: 456, - Float: 123.456, - Bool: true, + String: "str!", + Bytes: []byte("bytes!"), + Int: 123, + Int64: 123456789123456789, + Uint: 456, + Uint64: 987654321987654321, + Float: 123.456, + Float64: 1.2345678912345678e+17, + Bool: true, })) // Scan a different type with the same values to test that @@ -85,12 +143,15 @@ var _ = Describe("Scan", func() { Expect(Scan(&d, i{"string", "float", "bool"}, i{"", "1", "t"})).NotTo(HaveOccurred()) Expect(d).To(Equal(data{ - String: "", - Bytes: []byte("bytes!"), - Int: 123, - Uint: 456, - Float: 1.0, - Bool: true, + String: "", + Bytes: []byte("bytes!"), + Int: 123, + Int64: 123456789123456789, + Uint: 456, + Uint64: 987654321987654321, + Float: 1.0, + Float64: 1.2345678912345678e+17, + Bool: true, })) }) diff --git a/internal/hscan/structmap.go b/internal/hscan/structmap.go index 37d86ba..1b23325 100644 --- a/internal/hscan/structmap.go +++ b/internal/hscan/structmap.go @@ -1,6 +1,7 @@ package hscan import ( + "fmt" "reflect" "strings" "sync" @@ -83,5 +84,10 @@ func (s StructValue) Scan(key string, value string) error { if !ok { return nil } - return field.fn(s.value.Field(field.index), value) + if err := field.fn(s.value.Field(field.index), value); err != nil { + t := s.value.Type() + return fmt.Errorf("cannot scan redis.result %s into struct field %s.%s of type %s, error-%s", + value, t.Name(), t.Field(field.index).Name, t.Field(field.index).Type, err.Error()) + } + return nil }