mirror of
https://github.com/redis/go-redis.git
synced 2025-04-19 07:22:17 +03:00
The package uses reflection to decode default types (int, string etc.) from Redis map results (key-value pair sequences) into struct fields where the fields are matched to Redis keys by tags. Similar to how `encoding/json` allows custom decoders using `UnmarshalJSON()`, the package supports decoding of arbitrary types into struct fields by defining a `Decode(string) error` function on types. The field/type spec of every struct that's passed to Scan() is cached in the package so that subsequent scans avoid iteration and reflection of the struct's fields.
88 lines
1.5 KiB
Go
88 lines
1.5 KiB
Go
package hscan
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// structField represents a single field in a target struct.
|
|
type structField struct {
|
|
index int
|
|
fn decoderFunc
|
|
}
|
|
|
|
// structFields contains the list of all fields in a target struct.
|
|
type structFields struct {
|
|
m map[string]*structField
|
|
}
|
|
|
|
// 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 &structMap{
|
|
m: sync.Map{},
|
|
}
|
|
}
|
|
|
|
func (s *structMap) get(t reflect.Type) (*structFields, bool) {
|
|
m, ok := s.m.Load(t)
|
|
if !ok {
|
|
return nil, ok
|
|
}
|
|
|
|
return m.(*structFields), true
|
|
}
|
|
|
|
func (s *structMap) set(t reflect.Type, sf *structFields) {
|
|
s.m.Store(t, sf)
|
|
}
|
|
|
|
func newStructFields() *structFields {
|
|
return &structFields{
|
|
m: make(map[string]*structField),
|
|
}
|
|
}
|
|
|
|
func (s *structFields) set(tag string, sf *structField) {
|
|
s.m[tag] = sf
|
|
}
|
|
|
|
func (s *structFields) get(tag string) (*structField, bool) {
|
|
f, ok := s.m[tag]
|
|
return f, ok
|
|
}
|
|
|
|
func makeStructSpecs(ob reflect.Value, fieldTag string) *structFields {
|
|
var (
|
|
num = ob.NumField()
|
|
out = newStructFields()
|
|
)
|
|
|
|
for i := 0; i < num; i++ {
|
|
f := ob.Field(i)
|
|
if !f.IsValid() || !f.CanSet() {
|
|
continue
|
|
}
|
|
|
|
tag := ob.Type().Field(i).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.Kind()]})
|
|
}
|
|
|
|
return out
|
|
}
|