blob: 08d18d35e43e5fb23060d75dfb4c65a85e304830 [file] [log] [blame]
Joey Armstrong5f51f2e2023-01-17 17:06:26 -05001package proto
2
3import (
4 "encoding"
5 "fmt"
6 "reflect"
7 "time"
8
9 "github.com/go-redis/redis/v8/internal/util"
10)
11
12// Scan parses bytes `b` to `v` with appropriate type.
13// nolint: gocyclo
14func Scan(b []byte, v interface{}) error {
15 switch v := v.(type) {
16 case nil:
17 return fmt.Errorf("redis: Scan(nil)")
18 case *string:
19 *v = util.BytesToString(b)
20 return nil
21 case *[]byte:
22 *v = b
23 return nil
24 case *int:
25 var err error
26 *v, err = util.Atoi(b)
27 return err
28 case *int8:
29 n, err := util.ParseInt(b, 10, 8)
30 if err != nil {
31 return err
32 }
33 *v = int8(n)
34 return nil
35 case *int16:
36 n, err := util.ParseInt(b, 10, 16)
37 if err != nil {
38 return err
39 }
40 *v = int16(n)
41 return nil
42 case *int32:
43 n, err := util.ParseInt(b, 10, 32)
44 if err != nil {
45 return err
46 }
47 *v = int32(n)
48 return nil
49 case *int64:
50 n, err := util.ParseInt(b, 10, 64)
51 if err != nil {
52 return err
53 }
54 *v = n
55 return nil
56 case *uint:
57 n, err := util.ParseUint(b, 10, 64)
58 if err != nil {
59 return err
60 }
61 *v = uint(n)
62 return nil
63 case *uint8:
64 n, err := util.ParseUint(b, 10, 8)
65 if err != nil {
66 return err
67 }
68 *v = uint8(n)
69 return nil
70 case *uint16:
71 n, err := util.ParseUint(b, 10, 16)
72 if err != nil {
73 return err
74 }
75 *v = uint16(n)
76 return nil
77 case *uint32:
78 n, err := util.ParseUint(b, 10, 32)
79 if err != nil {
80 return err
81 }
82 *v = uint32(n)
83 return nil
84 case *uint64:
85 n, err := util.ParseUint(b, 10, 64)
86 if err != nil {
87 return err
88 }
89 *v = n
90 return nil
91 case *float32:
92 n, err := util.ParseFloat(b, 32)
93 if err != nil {
94 return err
95 }
96 *v = float32(n)
97 return err
98 case *float64:
99 var err error
100 *v, err = util.ParseFloat(b, 64)
101 return err
102 case *bool:
103 *v = len(b) == 1 && b[0] == '1'
104 return nil
105 case *time.Time:
106 var err error
107 *v, err = time.Parse(time.RFC3339Nano, util.BytesToString(b))
108 return err
109 case encoding.BinaryUnmarshaler:
110 return v.UnmarshalBinary(b)
111 default:
112 return fmt.Errorf(
113 "redis: can't unmarshal %T (consider implementing BinaryUnmarshaler)", v)
114 }
115}
116
117func ScanSlice(data []string, slice interface{}) error {
118 v := reflect.ValueOf(slice)
119 if !v.IsValid() {
120 return fmt.Errorf("redis: ScanSlice(nil)")
121 }
122 if v.Kind() != reflect.Ptr {
123 return fmt.Errorf("redis: ScanSlice(non-pointer %T)", slice)
124 }
125 v = v.Elem()
126 if v.Kind() != reflect.Slice {
127 return fmt.Errorf("redis: ScanSlice(non-slice %T)", slice)
128 }
129
130 next := makeSliceNextElemFunc(v)
131 for i, s := range data {
132 elem := next()
133 if err := Scan([]byte(s), elem.Addr().Interface()); err != nil {
134 err = fmt.Errorf("redis: ScanSlice index=%d value=%q failed: %s", i, s, err)
135 return err
136 }
137 }
138
139 return nil
140}
141
142func makeSliceNextElemFunc(v reflect.Value) func() reflect.Value {
143 elemType := v.Type().Elem()
144
145 if elemType.Kind() == reflect.Ptr {
146 elemType = elemType.Elem()
147 return func() reflect.Value {
148 if v.Len() < v.Cap() {
149 v.Set(v.Slice(0, v.Len()+1))
150 elem := v.Index(v.Len() - 1)
151 if elem.IsNil() {
152 elem.Set(reflect.New(elemType))
153 }
154 return elem.Elem()
155 }
156
157 elem := reflect.New(elemType)
158 v.Set(reflect.Append(v, elem))
159 return elem.Elem()
160 }
161 }
162
163 zero := reflect.Zero(elemType)
164 return func() reflect.Value {
165 if v.Len() < v.Cap() {
166 v.Set(v.Slice(0, v.Len()+1))
167 return v.Index(v.Len() - 1)
168 }
169
170 v.Set(reflect.Append(v, zero))
171 return v.Index(v.Len() - 1)
172 }
173}