Go API Documentation

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

Uses: errors.New.

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

Uses: fmt.Errorf, reflect.Ptr, reflect.Struct, reflect.ValueOf.

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{}

Test functions

TestGinkgoSuite