Skip to content

Commit f9dfc7a

Browse files
committed
Refactor scan signature to work with Slice and StringMap cmds
1 parent a4144ea commit f9dfc7a

File tree

4 files changed

+74
-47
lines changed

4 files changed

+74
-47
lines changed

command.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -372,11 +372,12 @@ func (cmd *SliceCmd) String() string {
372372
return cmdString(cmd, cmd.val)
373373
}
374374

375-
// Scan scans the results from a key-value Redis map result set ([]interface{})
376-
// like HMGET and HGETALL to a destination struct.
377-
// The Redis keys are matched to the struct's field with the `redis` tag.
378-
func (cmd *SliceCmd) Scan(val interface{}) error {
379-
return hscan.Scan(cmd.val, val)
375+
// Scan scans the results from the map into a destination struct. The map keys
376+
// are matched in the Redis struct fields by the `redis:"field"` tag.
377+
func (cmd *SliceCmd) Scan(dest interface{}) error {
378+
// Pass the list of keys and values. Skip the first to args (command, key),
379+
// eg: HMGET map.
380+
return hscan.Scan(cmd.args[2:], cmd.val, dest)
380381
}
381382

382383
func (cmd *SliceCmd) readReply(rd *proto.Reader) error {
@@ -925,6 +926,23 @@ func (cmd *StringStringMapCmd) String() string {
925926
return cmdString(cmd, cmd.val)
926927
}
927928

929+
// Scan scans the results from the map into a destination struct. The map keys
930+
// are matched in the Redis struct fields by the `redis:"field"` tag.
931+
func (cmd *StringStringMapCmd) Scan(dest interface{}) error {
932+
// Pass the list of keys and values. Skip the first to args (command, key),
933+
// eg: HGETALL map.
934+
var (
935+
keys = make([]interface{}, 0, len(cmd.val))
936+
vals = make([]interface{}, 0, len(cmd.val))
937+
)
938+
for k, v := range cmd.val {
939+
keys = append(keys, k)
940+
vals = append(vals, v)
941+
}
942+
943+
return hscan.Scan(keys, vals, dest)
944+
}
945+
928946
func (cmd *StringStringMapCmd) readReply(rd *proto.Reader) error {
929947
_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
930948
cmd.val = make(map[string]string, n/2)

commands_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,6 +1375,22 @@ var _ = Describe("Commands", func() {
13751375
Expect(m).To(Equal(map[string]string{"key1": "hello1", "key2": "hello2"}))
13761376
})
13771377

1378+
It("should scan", func() {
1379+
err := client.HMSet(ctx, "hash", "key1", "hello1", "key2", 123).Err()
1380+
Expect(err).NotTo(HaveOccurred())
1381+
1382+
res := client.HGetAll(ctx, "hash")
1383+
Expect(res.Err()).NotTo(HaveOccurred())
1384+
1385+
type data struct {
1386+
Key1 string `redis:"key1"`
1387+
Key2 int `redis:"key2"`
1388+
}
1389+
var d data
1390+
Expect(res.Scan(&d)).NotTo(HaveOccurred())
1391+
Expect(d).To(Equal(data{Key1: "hello1", Key2: 123}))
1392+
})
1393+
13781394
It("should HIncrBy", func() {
13791395
hSet := client.HSet(ctx, "hash", "key", "5")
13801396
Expect(hSet.Err()).NotTo(HaveOccurred())

internal/hscan/hscan.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,11 @@ var (
4646
structSpecs = newStructMap()
4747
)
4848

49-
// Scan scans the results from a key-value Redis map result set ([]interface{})
50-
// to a destination struct. The Redis keys are matched to the struct's field
51-
// with the `redis` tag.
52-
func Scan(vals []interface{}, dest interface{}) error {
53-
if len(vals)%2 != 0 {
54-
return errors.New("args should have an even number of items (key-val)")
49+
// Scan scans the results from a key-value Redis map result set to a destination struct.
50+
// The Redis keys are matched to the struct's field with the `redis` tag.
51+
func Scan(keys []interface{}, vals []interface{}, dest interface{}) error {
52+
if len(keys) != len(vals) {
53+
return errors.New("args should have the same number of keys and vals")
5554
}
5655

5756
// The destination to scan into should be a struct pointer.
@@ -72,13 +71,13 @@ func Scan(vals []interface{}, dest interface{}) error {
7271
fMap := structSpecs.get(typ)
7372

7473
// Iterate through the (key, value) sequence.
75-
for i := 0; i < len(vals); i += 2 {
76-
key, ok := vals[i].(string)
74+
for i := 0; i < len(vals); i++ {
75+
key, ok := keys[i].(string)
7776
if !ok {
7877
continue
7978
}
8079

81-
val, ok := vals[i+1].(string)
80+
val, ok := vals[i].(string)
8281
if !ok {
8382
continue
8483
}

internal/hscan/hscan_test.go

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,42 +19,42 @@ type data struct {
1919
Bool bool `redis:"bool"`
2020
}
2121

22+
type i []interface{}
23+
2224
func TestGinkgoSuite(t *testing.T) {
2325
RegisterFailHandler(Fail)
2426
RunSpecs(t, "hscan")
2527
}
2628

2729
var _ = Describe("Scan", func() {
2830
It("catches bad args", func() {
29-
var d data
31+
var (
32+
d data
33+
)
3034

31-
Expect(Scan([]interface{}{}, &d)).NotTo(HaveOccurred())
35+
Expect(Scan(i{}, i{}, &d)).NotTo(HaveOccurred())
3236
Expect(d).To(Equal(data{}))
3337

34-
Expect(Scan([]interface{}{"key"}, &d)).To(HaveOccurred())
35-
Expect(Scan([]interface{}{"key", "1", "2"}, &d)).To(HaveOccurred())
36-
Expect(Scan([]interface{}{"key", "1"}, nil)).To(HaveOccurred())
38+
Expect(Scan(i{"key"}, i{}, &d)).To(HaveOccurred())
39+
Expect(Scan(i{"key"}, i{"1", "2"}, &d)).To(HaveOccurred())
40+
Expect(Scan(i{"key", "1"}, i{}, nil)).To(HaveOccurred())
3741

38-
var i map[string]interface{}
39-
Expect(Scan([]interface{}{"key", "1"}, &i)).To(HaveOccurred())
40-
Expect(Scan([]interface{}{"key", "1"}, data{})).To(HaveOccurred())
41-
Expect(Scan([]interface{}{"key", nil, "string", nil}, data{})).To(HaveOccurred())
42+
var m map[string]interface{}
43+
Expect(Scan(i{"key"}, i{"1"}, &m)).To(HaveOccurred())
44+
Expect(Scan(i{"key"}, i{"1"}, data{})).To(HaveOccurred())
45+
Expect(Scan(i{"key", "string"}, i{nil, nil}, data{})).To(HaveOccurred())
4246
})
4347

4448
It("scans good values", func() {
4549
var d data
4650

4751
// non-tagged fields.
48-
Expect(Scan([]interface{}{"key", "value"}, &d)).NotTo(HaveOccurred())
52+
Expect(Scan(i{"key"}, i{"value"}, &d)).NotTo(HaveOccurred())
4953
Expect(d).To(Equal(data{}))
5054

51-
res := []interface{}{"string", "str!",
52-
"byte", "bytes!",
53-
"int", "123",
54-
"uint", "456",
55-
"float", "123.456",
56-
"bool", "1"}
57-
Expect(Scan(res, &d)).NotTo(HaveOccurred())
55+
keys := i{"string", "byte", "int", "uint", "float", "bool"}
56+
vals := i{"str!", "bytes!", "123", "456", "123.456", "1"}
57+
Expect(Scan(keys, vals, &d)).NotTo(HaveOccurred())
5858
Expect(d).To(Equal(data{
5959
String: "str!",
6060
Bytes: []byte("bytes!"),
@@ -75,7 +75,7 @@ var _ = Describe("Scan", func() {
7575
Bool bool `redis:"bool"`
7676
}
7777
var d2 data2
78-
Expect(Scan(res, &d2)).NotTo(HaveOccurred())
78+
Expect(Scan(keys, vals, &d2)).NotTo(HaveOccurred())
7979
Expect(d2).To(Equal(data2{
8080
String: "str!",
8181
Bytes: []byte("bytes!"),
@@ -85,10 +85,7 @@ var _ = Describe("Scan", func() {
8585
Bool: true,
8686
}))
8787

88-
Expect(Scan([]interface{}{
89-
"string", "",
90-
"float", "1",
91-
"bool", "t"}, &d)).NotTo(HaveOccurred())
88+
Expect(Scan(i{"string", "float", "bool"}, i{"", "1", "t"}, &d)).NotTo(HaveOccurred())
9289
Expect(d).To(Equal(data{
9390
String: "",
9491
Bytes: []byte("bytes!"),
@@ -102,10 +99,7 @@ var _ = Describe("Scan", func() {
10299
It("omits untagged fields", func() {
103100
var d data
104101

105-
Expect(Scan([]interface{}{
106-
"empty", "value",
107-
"omit", "value",
108-
"string", "str!"}, &d)).NotTo(HaveOccurred())
102+
Expect(Scan(i{"empty", "omit", "string"}, i{"value", "value", "str!"}, &d)).NotTo(HaveOccurred())
109103
Expect(d).To(Equal(data{
110104
String: "str!",
111105
}))
@@ -114,12 +108,12 @@ var _ = Describe("Scan", func() {
114108
It("catches bad values", func() {
115109
var d data
116110

117-
Expect(Scan([]interface{}{"int", "a"}, &d)).To(HaveOccurred())
118-
Expect(Scan([]interface{}{"uint", "a"}, &d)).To(HaveOccurred())
119-
Expect(Scan([]interface{}{"uint", ""}, &d)).To(HaveOccurred())
120-
Expect(Scan([]interface{}{"float", "b"}, &d)).To(HaveOccurred())
121-
Expect(Scan([]interface{}{"bool", "-1"}, &d)).To(HaveOccurred())
122-
Expect(Scan([]interface{}{"bool", ""}, &d)).To(HaveOccurred())
123-
Expect(Scan([]interface{}{"bool", "123"}, &d)).To(HaveOccurred())
111+
Expect(Scan(i{"int"}, i{"a"}, &d)).To(HaveOccurred())
112+
Expect(Scan(i{"uint"}, i{"a"}, &d)).To(HaveOccurred())
113+
Expect(Scan(i{"uint"}, i{""}, &d)).To(HaveOccurred())
114+
Expect(Scan(i{"float"}, i{"b"}, &d)).To(HaveOccurred())
115+
Expect(Scan(i{"bool"}, i{"-1"}, &d)).To(HaveOccurred())
116+
Expect(Scan(i{"bool"}, i{""}, &d)).To(HaveOccurred())
117+
Expect(Scan(i{"bool"}, i{"123"}, &d)).To(HaveOccurred())
124118
})
125119
})

0 commit comments

Comments
 (0)