blob: 852c8bd525a7fe9c64746c5439caa830b4cd6e65 [file] [log] [blame]
Naveen Sampath04696f72022-06-13 15:19:14 +05301package hscan
2
3import (
4 "errors"
5 "fmt"
6 "reflect"
7 "strconv"
8)
9
10// decoderFunc represents decoding functions for default built-in types.
11type decoderFunc func(reflect.Value, string) error
12
13var (
14 // List of built-in decoders indexed by their numeric constant values (eg: reflect.Bool = 1).
15 decoders = []decoderFunc{
16 reflect.Bool: decodeBool,
17 reflect.Int: decodeInt,
18 reflect.Int8: decodeInt8,
19 reflect.Int16: decodeInt16,
20 reflect.Int32: decodeInt32,
21 reflect.Int64: decodeInt64,
22 reflect.Uint: decodeUint,
23 reflect.Uint8: decodeUint8,
24 reflect.Uint16: decodeUint16,
25 reflect.Uint32: decodeUint32,
26 reflect.Uint64: decodeUint64,
27 reflect.Float32: decodeFloat32,
28 reflect.Float64: decodeFloat64,
29 reflect.Complex64: decodeUnsupported,
30 reflect.Complex128: decodeUnsupported,
31 reflect.Array: decodeUnsupported,
32 reflect.Chan: decodeUnsupported,
33 reflect.Func: decodeUnsupported,
34 reflect.Interface: decodeUnsupported,
35 reflect.Map: decodeUnsupported,
36 reflect.Ptr: decodeUnsupported,
37 reflect.Slice: decodeSlice,
38 reflect.String: decodeString,
39 reflect.Struct: decodeUnsupported,
40 reflect.UnsafePointer: decodeUnsupported,
41 }
42
43 // Global map of struct field specs that is populated once for every new
44 // struct type that is scanned. This caches the field types and the corresponding
45 // decoder functions to avoid iterating through struct fields on subsequent scans.
46 globalStructMap = newStructMap()
47)
48
49func Struct(dst interface{}) (StructValue, error) {
50 v := reflect.ValueOf(dst)
51
52 // The destination to scan into should be a struct pointer.
53 if v.Kind() != reflect.Ptr || v.IsNil() {
54 return StructValue{}, fmt.Errorf("redis.Scan(non-pointer %T)", dst)
55 }
56
57 v = v.Elem()
58 if v.Kind() != reflect.Struct {
59 return StructValue{}, fmt.Errorf("redis.Scan(non-struct %T)", dst)
60 }
61
62 return StructValue{
63 spec: globalStructMap.get(v.Type()),
64 value: v,
65 }, nil
66}
67
68// Scan scans the results from a key-value Redis map result set to a destination struct.
69// The Redis keys are matched to the struct's field with the `redis` tag.
70func Scan(dst interface{}, keys []interface{}, vals []interface{}) error {
71 if len(keys) != len(vals) {
72 return errors.New("args should have the same number of keys and vals")
73 }
74
75 strct, err := Struct(dst)
76 if err != nil {
77 return err
78 }
79
80 // Iterate through the (key, value) sequence.
81 for i := 0; i < len(vals); i++ {
82 key, ok := keys[i].(string)
83 if !ok {
84 continue
85 }
86
87 val, ok := vals[i].(string)
88 if !ok {
89 continue
90 }
91
92 if err := strct.Scan(key, val); err != nil {
93 return err
94 }
95 }
96
97 return nil
98}
99
100func decodeBool(f reflect.Value, s string) error {
101 b, err := strconv.ParseBool(s)
102 if err != nil {
103 return err
104 }
105 f.SetBool(b)
106 return nil
107}
108
109func decodeInt8(f reflect.Value, s string) error {
110 return decodeNumber(f, s, 8)
111}
112
113func decodeInt16(f reflect.Value, s string) error {
114 return decodeNumber(f, s, 16)
115}
116
117func decodeInt32(f reflect.Value, s string) error {
118 return decodeNumber(f, s, 32)
119}
120
121func decodeInt64(f reflect.Value, s string) error {
122 return decodeNumber(f, s, 64)
123}
124
125func decodeInt(f reflect.Value, s string) error {
126 return decodeNumber(f, s, 0)
127}
128
129func decodeNumber(f reflect.Value, s string, bitSize int) error {
130 v, err := strconv.ParseInt(s, 10, bitSize)
131 if err != nil {
132 return err
133 }
134 f.SetInt(v)
135 return nil
136}
137
138func decodeUint8(f reflect.Value, s string) error {
139 return decodeUnsignedNumber(f, s, 8)
140}
141
142func decodeUint16(f reflect.Value, s string) error {
143 return decodeUnsignedNumber(f, s, 16)
144}
145
146func decodeUint32(f reflect.Value, s string) error {
147 return decodeUnsignedNumber(f, s, 32)
148}
149
150func decodeUint64(f reflect.Value, s string) error {
151 return decodeUnsignedNumber(f, s, 64)
152}
153
154func decodeUint(f reflect.Value, s string) error {
155 return decodeUnsignedNumber(f, s, 0)
156}
157
158func decodeUnsignedNumber(f reflect.Value, s string, bitSize int) error {
159 v, err := strconv.ParseUint(s, 10, bitSize)
160 if err != nil {
161 return err
162 }
163 f.SetUint(v)
164 return nil
165}
166
167func decodeFloat32(f reflect.Value, s string) error {
168 v, err := strconv.ParseFloat(s, 32)
169 if err != nil {
170 return err
171 }
172 f.SetFloat(v)
173 return nil
174}
175
176// although the default is float64, but we better define it.
177func decodeFloat64(f reflect.Value, s string) error {
178 v, err := strconv.ParseFloat(s, 64)
179 if err != nil {
180 return err
181 }
182 f.SetFloat(v)
183 return nil
184}
185
186func decodeString(f reflect.Value, s string) error {
187 f.SetString(s)
188 return nil
189}
190
191func decodeSlice(f reflect.Value, s string) error {
192 // []byte slice ([]uint8).
193 if f.Type().Elem().Kind() == reflect.Uint8 {
194 f.SetBytes([]byte(s))
195 }
196 return nil
197}
198
199func decodeUnsupported(v reflect.Value, s string) error {
200 return fmt.Errorf("redis.Scan(unsupported %s)", v.Type())
201}