...
1 package hscan
2
3 import (
4 "encoding"
5 "fmt"
6 "reflect"
7 "strings"
8 "sync"
9
10 "github.com/redis/go-redis/v9/internal/util"
11 )
12
13
14
15 type structMap struct {
16 m sync.Map
17 }
18
19 func newStructMap() *structMap {
20 return new(structMap)
21 }
22
23 func (s *structMap) get(t reflect.Type) *structSpec {
24 if v, ok := s.m.Load(t); ok {
25 return v.(*structSpec)
26 }
27
28 spec := newStructSpec(t, "redis")
29 s.m.Store(t, spec)
30 return spec
31 }
32
33
34
35
36 type structSpec struct {
37 m map[string]*structField
38 }
39
40 func (s *structSpec) set(tag string, sf *structField) {
41 s.m[tag] = sf
42 }
43
44 func newStructSpec(t reflect.Type, fieldTag string) *structSpec {
45 numField := t.NumField()
46 out := &structSpec{
47 m: make(map[string]*structField, numField),
48 }
49
50 for i := 0; i < numField; i++ {
51 f := t.Field(i)
52
53 tag := f.Tag.Get(fieldTag)
54 if tag == "" || tag == "-" {
55 continue
56 }
57
58 tag = strings.Split(tag, ",")[0]
59 if tag == "" {
60 continue
61 }
62
63
64 kind := f.Type.Kind()
65 if kind == reflect.Pointer {
66 kind = f.Type.Elem().Kind()
67 }
68 out.set(tag, &structField{index: i, fn: decoders[kind]})
69 }
70
71 return out
72 }
73
74
75
76
77 type structField struct {
78 index int
79 fn decoderFunc
80 }
81
82
83
84 type StructValue struct {
85 spec *structSpec
86 value reflect.Value
87 }
88
89 func (s StructValue) Scan(key string, value string) error {
90 field, ok := s.spec.m[key]
91 if !ok {
92 return nil
93 }
94
95 v := s.value.Field(field.index)
96 isPtr := v.Kind() == reflect.Ptr
97
98 if isPtr && v.IsNil() {
99 v.Set(reflect.New(v.Type().Elem()))
100 }
101 if !isPtr && v.Type().Name() != "" && v.CanAddr() {
102 v = v.Addr()
103 isPtr = true
104 }
105
106 if isPtr && v.Type().NumMethod() > 0 && v.CanInterface() {
107 switch scan := v.Interface().(type) {
108 case Scanner:
109 return scan.ScanRedis(value)
110 case encoding.TextUnmarshaler:
111 return scan.UnmarshalText(util.StringToBytes(value))
112 }
113 }
114
115 if isPtr {
116 v = v.Elem()
117 }
118
119 if err := field.fn(v, value); err != nil {
120 t := s.value.Type()
121 return fmt.Errorf("cannot scan redis.result %s into struct field %s.%s of type %s, error-%s",
122 value, t.Name(), t.Field(field.index).Name, t.Field(field.index).Type, err.Error())
123 }
124 return nil
125 }
126
View as plain text