summaryrefslogtreecommitdiffstats
path: root/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/hscan.go201
-rw-r--r--dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/hscan_test.go178
-rw-r--r--dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/structmap.go93
3 files changed, 472 insertions, 0 deletions
diff --git a/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/hscan.go b/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/hscan.go
new file mode 100644
index 0000000..852c8bd
--- /dev/null
+++ b/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/hscan.go
@@ -0,0 +1,201 @@
+package hscan
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+)
+
+// decoderFunc represents decoding functions for default built-in types.
+type decoderFunc func(reflect.Value, string) error
+
+var (
+ // List of built-in decoders indexed by their numeric constant values (eg: reflect.Bool = 1).
+ decoders = []decoderFunc{
+ reflect.Bool: decodeBool,
+ reflect.Int: decodeInt,
+ reflect.Int8: decodeInt8,
+ reflect.Int16: decodeInt16,
+ reflect.Int32: decodeInt32,
+ reflect.Int64: decodeInt64,
+ reflect.Uint: decodeUint,
+ 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,
+ reflect.Chan: decodeUnsupported,
+ reflect.Func: decodeUnsupported,
+ reflect.Interface: decodeUnsupported,
+ reflect.Map: decodeUnsupported,
+ reflect.Ptr: decodeUnsupported,
+ reflect.Slice: decodeSlice,
+ reflect.String: decodeString,
+ reflect.Struct: decodeUnsupported,
+ reflect.UnsafePointer: decodeUnsupported,
+ }
+
+ // Global map of struct field specs that is populated once for every new
+ // struct type that is scanned. This caches the field types and the corresponding
+ // decoder functions to avoid iterating through struct fields on subsequent scans.
+ globalStructMap = newStructMap()
+)
+
+func Struct(dst interface{}) (StructValue, error) {
+ v := reflect.ValueOf(dst)
+
+ // The destination to scan into should be a struct pointer.
+ if v.Kind() != reflect.Ptr || v.IsNil() {
+ return StructValue{}, fmt.Errorf("redis.Scan(non-pointer %T)", dst)
+ }
+
+ v = v.Elem()
+ if v.Kind() != reflect.Struct {
+ return StructValue{}, fmt.Errorf("redis.Scan(non-struct %T)", dst)
+ }
+
+ return StructValue{
+ spec: globalStructMap.get(v.Type()),
+ value: v,
+ }, nil
+}
+
+// Scan scans the results from a key-value Redis map result set to a destination struct.
+// The Redis keys are matched to the struct's field with the `redis` tag.
+func Scan(dst interface{}, keys []interface{}, vals []interface{}) error {
+ if len(keys) != len(vals) {
+ return errors.New("args should have the same number of keys and vals")
+ }
+
+ strct, err := Struct(dst)
+ if err != nil {
+ return err
+ }
+
+ // Iterate through the (key, value) sequence.
+ for i := 0; i < len(vals); i++ {
+ key, ok := keys[i].(string)
+ if !ok {
+ continue
+ }
+
+ val, ok := vals[i].(string)
+ if !ok {
+ continue
+ }
+
+ if err := strct.Scan(key, val); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func decodeBool(f reflect.Value, s string) error {
+ b, err := strconv.ParseBool(s)
+ if err != nil {
+ return err
+ }
+ f.SetBool(b)
+ 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 {
+ 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
+ }
+ f.SetInt(v)
+ 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 {
+ 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
+ }
+ f.SetUint(v)
+ return nil
+}
+
+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
+ }
+ f.SetFloat(v)
+ return nil
+}
+
+func decodeString(f reflect.Value, s string) error {
+ f.SetString(s)
+ return nil
+}
+
+func decodeSlice(f reflect.Value, s string) error {
+ // []byte slice ([]uint8).
+ if f.Type().Elem().Kind() == reflect.Uint8 {
+ f.SetBytes([]byte(s))
+ }
+ return nil
+}
+
+func decodeUnsupported(v reflect.Value, s string) error {
+ return fmt.Errorf("redis.Scan(unsupported %s)", v.Type())
+}
diff --git a/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/hscan_test.go b/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/hscan_test.go
new file mode 100644
index 0000000..ab4c0e1
--- /dev/null
+++ b/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/hscan_test.go
@@ -0,0 +1,178 @@
+package hscan
+
+import (
+ "math"
+ "strconv"
+ "testing"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+type data struct {
+ Omit string `redis:"-"`
+ Empty string
+
+ 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{}
+
+func TestGinkgoSuite(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "hscan")
+}
+
+var _ = Describe("Scan", func() {
+ It("catches bad args", func() {
+ var d data
+
+ Expect(Scan(&d, i{}, i{})).NotTo(HaveOccurred())
+ Expect(d).To(Equal(data{}))
+
+ Expect(Scan(&d, i{"key"}, i{})).To(HaveOccurred())
+ Expect(Scan(&d, i{"key"}, i{"1", "2"})).To(HaveOccurred())
+ Expect(Scan(nil, i{"key", "1"}, i{})).To(HaveOccurred())
+
+ var m map[string]interface{}
+ Expect(Scan(&m, i{"key"}, i{"1"})).To(HaveOccurred())
+ Expect(Scan(data{}, i{"key"}, i{"1"})).To(HaveOccurred())
+ 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
+
+ // non-tagged fields.
+ Expect(Scan(&d, i{"key"}, i{"value"})).NotTo(HaveOccurred())
+ Expect(d).To(Equal(data{}))
+
+ 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,
+ 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
+ // the struct spec maps don't conflict.
+ type data2 struct {
+ String string `redis:"string"`
+ Bytes []byte `redis:"byte"`
+ Int int `redis:"int"`
+ Uint uint `redis:"uint"`
+ Float float32 `redis:"float"`
+ Bool bool `redis:"bool"`
+ }
+ var d2 data2
+ Expect(Scan(&d2, keys, vals)).NotTo(HaveOccurred())
+ Expect(d2).To(Equal(data2{
+ String: "str!",
+ Bytes: []byte("bytes!"),
+ Int: 123,
+ Uint: 456,
+ Float: 123.456,
+ Bool: true,
+ }))
+
+ Expect(Scan(&d, i{"string", "float", "bool"}, i{"", "1", "t"})).NotTo(HaveOccurred())
+ Expect(d).To(Equal(data{
+ String: "",
+ Bytes: []byte("bytes!"),
+ Int: 123,
+ Int64: 123456789123456789,
+ Uint: 456,
+ Uint64: 987654321987654321,
+ Float: 1.0,
+ Float64: 1.2345678912345678e+17,
+ Bool: true,
+ }))
+ })
+
+ It("omits untagged fields", func() {
+ var d data
+
+ Expect(Scan(&d, i{"empty", "omit", "string"}, i{"value", "value", "str!"})).NotTo(HaveOccurred())
+ Expect(d).To(Equal(data{
+ String: "str!",
+ }))
+ })
+
+ It("catches bad values", func() {
+ var d data
+
+ Expect(Scan(&d, i{"int"}, i{"a"})).To(HaveOccurred())
+ Expect(Scan(&d, i{"uint"}, i{"a"})).To(HaveOccurred())
+ Expect(Scan(&d, i{"uint"}, i{""})).To(HaveOccurred())
+ Expect(Scan(&d, i{"float"}, i{"b"})).To(HaveOccurred())
+ Expect(Scan(&d, i{"bool"}, i{"-1"})).To(HaveOccurred())
+ Expect(Scan(&d, i{"bool"}, i{""})).To(HaveOccurred())
+ Expect(Scan(&d, i{"bool"}, i{"123"})).To(HaveOccurred())
+ })
+})
diff --git a/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/structmap.go b/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/structmap.go
new file mode 100644
index 0000000..6839412
--- /dev/null
+++ b/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/structmap.go
@@ -0,0 +1,93 @@
+package hscan
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+ "sync"
+)
+
+// structMap contains the map of struct fields for target structs
+// indexed by the struct type.
+type structMap struct {
+ m sync.Map
+}
+
+func newStructMap() *structMap {
+ return new(structMap)
+}
+
+func (s *structMap) get(t reflect.Type) *structSpec {
+ if v, ok := s.m.Load(t); ok {
+ return v.(*structSpec)
+ }
+
+ spec := newStructSpec(t, "redis")
+ s.m.Store(t, spec)
+ return spec
+}
+
+//------------------------------------------------------------------------------
+
+// structSpec contains the list of all fields in a target struct.
+type structSpec struct {
+ m map[string]*structField
+}
+
+func (s *structSpec) set(tag string, sf *structField) {
+ s.m[tag] = sf
+}
+
+func newStructSpec(t reflect.Type, fieldTag string) *structSpec {
+ numField := t.NumField()
+ out := &structSpec{
+ m: make(map[string]*structField, numField),
+ }
+
+ for i := 0; i < numField; i++ {
+ f := t.Field(i)
+
+ tag := f.Tag.Get(fieldTag)
+ if tag == "" || tag == "-" {
+ continue
+ }
+
+ tag = strings.Split(tag, ",")[0]
+ if tag == "" {
+ continue
+ }
+
+ // Use the built-in decoder.
+ out.set(tag, &structField{index: i, fn: decoders[f.Type.Kind()]})
+ }
+
+ return out
+}
+
+//------------------------------------------------------------------------------
+
+// structField represents a single field in a target struct.
+type structField struct {
+ index int
+ fn decoderFunc
+}
+
+//------------------------------------------------------------------------------
+
+type StructValue struct {
+ spec *structSpec
+ value reflect.Value
+}
+
+func (s StructValue) Scan(key string, value string) error {
+ field, ok := s.spec.m[key]
+ if !ok {
+ return nil
+ }
+ 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
+}