github.com/redis/go-redis/v9/internal/hscan
No package summary is available.
Package
Files: 2. Third party imports: 0. Imports from organisation: 0. Tests: 0. Benchmarks: 0.
Vars
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()
)
Types
Scanner
Scanner is the interface implemented by themselves, which will override the decoding behavior of decoderFunc.
type Scanner interface {
ScanRedis(s string) error
}
StructValue
This type doesn't have documentation.
type StructValue struct {
spec *structSpec
value reflect.Value
}
decoderFunc
decoderFunc represents decoding functions for default built-in types.
type decoderFunc func(reflect.Value, string) error
structField
structField represents a single field in a target struct.
type structField struct {
index int
fn decoderFunc
}
structMap
structMap contains the map of struct fields for target structs indexed by the struct type.
type structMap struct {
m sync.Map
}
structSpec
structSpec contains the list of all fields in a target struct.
type structSpec struct {
m map[string]*structField
}
Functions
func Scan
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
}
Cognitive complexity: 15
, Cyclomatic complexity: 7
func Struct
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
}
Cognitive complexity: 8
, Cyclomatic complexity: 4
Private functions
func decodeBool
decodeBool (f reflect.Value, s string) error
References: strconv.ParseBool.
func decodeFloat32
decodeFloat32 (f reflect.Value, s string) error
References: strconv.ParseFloat.
func decodeFloat64
although the default is float64, but we better define it.
decodeFloat64 (f reflect.Value, s string) error
References: strconv.ParseFloat.
func decodeInt
decodeInt (f reflect.Value, s string) error
func decodeInt16
decodeInt16 (f reflect.Value, s string) error
func decodeInt32
decodeInt32 (f reflect.Value, s string) error
func decodeInt64
decodeInt64 (f reflect.Value, s string) error
func decodeInt8
decodeInt8 (f reflect.Value, s string) error
func decodeNumber
decodeNumber (f reflect.Value, s string, bitSize int) error
References: strconv.ParseInt.
func decodeSlice
decodeSlice (f reflect.Value, s string) error
References: reflect.Uint8.
func decodeString
decodeString (f reflect.Value, s string) error
func decodeUint
decodeUint (f reflect.Value, s string) error
func decodeUint16
decodeUint16 (f reflect.Value, s string) error
func decodeUint32
decodeUint32 (f reflect.Value, s string) error
func decodeUint64
decodeUint64 (f reflect.Value, s string) error
func decodeUint8
decodeUint8 (f reflect.Value, s string) error
func decodeUnsignedNumber
decodeUnsignedNumber (f reflect.Value, s string, bitSize int) error
References: strconv.ParseUint.
func decodeUnsupported
decodeUnsupported (v reflect.Value, s string) error
References: fmt.Errorf.
func newStructMap
newStructMap () *structMap
func newStructSpec
newStructSpec (t reflect.Type, fieldTag string) *structSpec
References: reflect.Pointer, strings.Split.
func get
get (t reflect.Type) *structSpec
func set
set (tag string, sf *structField)
Tests
Files: 1. Third party imports: 2. Imports from organisation: 0. Tests: 1. Benchmarks: 0.
Vars
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", "boolRef"}
vals := i{
"str!", "bytes!", "123", "123456789123456789", "456", "987654321987654321",
"123.456", "123456789123456789.987654321987654321", "1", "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,
BoolRef: util.ToPtr(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,
BoolRef: util.ToPtr(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())
})
It("Implements Scanner", func() {
var td TimeData
now := time.Now()
Expect(Scan(&td, i{"name", "login"}, i{"hello", now.Format(time.RFC3339Nano)})).NotTo(HaveOccurred())
Expect(td.Name).To(Equal("hello"))
Expect(td.Time.UnixNano()).To(Equal(now.UnixNano()))
Expect(td.Time.Format(time.RFC3339Nano)).To(Equal(now.Format(time.RFC3339Nano)))
})
It("should time.Time RFC3339Nano", func() {
type TimeTime struct {
Time time.Time `redis:"time"`
}
now := time.Now()
var tt TimeTime
Expect(Scan(&tt, i{"time"}, i{now.Format(time.RFC3339Nano)})).NotTo(HaveOccurred())
Expect(now.Unix()).To(Equal(tt.Time.Unix()))
})
})
var d data
var d2 data2
var m map[string]interface{}
var td TimeData
var tt TimeTime
Types
TimeData
This type doesn't have documentation.
type TimeData struct {
Name string `redis:"name"`
Time *TimeRFC3339Nano `redis:"login"`
}
TimeRFC3339Nano
This type doesn't have documentation.
type TimeRFC3339Nano struct {
time.Time
}
TimeTime
This type doesn't have documentation.
type TimeTime struct {
Time time.Time `redis:"time"`
}
data
This type doesn't have documentation.
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"`
BoolRef *bool `redis:"boolRef"`
}
data2
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"`
}
i
This type doesn't have documentation.
type i []interface{}