Skip to content

Commit

Permalink
Add SliceCmd.Scan() (hscan pkg) and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
knadh committed Feb 2, 2021
1 parent 380ab17 commit a4144ea
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 2 deletions.
8 changes: 8 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"time"

"github.com/go-redis/redis/v8/internal"
"github.com/go-redis/redis/v8/internal/hscan"
"github.com/go-redis/redis/v8/internal/proto"
"github.com/go-redis/redis/v8/internal/util"
)
Expand Down Expand Up @@ -371,6 +372,13 @@ func (cmd *SliceCmd) String() string {
return cmdString(cmd, cmd.val)
}

// Scan scans the results from a key-value Redis map result set ([]interface{})
// like HMGET and HGETALL to a destination struct.
// The Redis keys are matched to the struct's field with the `redis` tag.
func (cmd *SliceCmd) Scan(val interface{}) error {
return hscan.Scan(cmd.val, val)
}

func (cmd *SliceCmd) readReply(rd *proto.Reader) error {
v, err := rd.ReadArrayReply(sliceParser)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/hscan/hscan.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
type decoderFunc func(reflect.Value, string) error

var (
// List of built-in decoders indexed by their numeric constant values (eg: reflect.Bool = 1)
// List of built-in decoders indexed by their numeric constant values (eg: reflect.Bool = 1).
decoders = []decoderFunc{
reflect.Bool: decodeBool,
reflect.Int: decodeInt,
Expand Down
125 changes: 125 additions & 0 deletions internal/hscan/hscan_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package hscan

import (
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

type data struct {
Omit string `redis:"-"`
Empty string

String string `redis:"string"`
Bytes []byte `redis:"byte"`
Int int `redis:"int"`
Uint uint `redis:"uint"`
Float float32 `redis:"float"`
Bool bool `redis:"bool"`
}

func TestGinkgoSuite(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "hscan")
}

var _ = Describe("Scan", func() {
It("catches bad args", func() {
var d data

Expect(Scan([]interface{}{}, &d)).NotTo(HaveOccurred())
Expect(d).To(Equal(data{}))

Expect(Scan([]interface{}{"key"}, &d)).To(HaveOccurred())
Expect(Scan([]interface{}{"key", "1", "2"}, &d)).To(HaveOccurred())
Expect(Scan([]interface{}{"key", "1"}, nil)).To(HaveOccurred())

var i map[string]interface{}
Expect(Scan([]interface{}{"key", "1"}, &i)).To(HaveOccurred())
Expect(Scan([]interface{}{"key", "1"}, data{})).To(HaveOccurred())
Expect(Scan([]interface{}{"key", nil, "string", nil}, data{})).To(HaveOccurred())
})

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

// non-tagged fields.
Expect(Scan([]interface{}{"key", "value"}, &d)).NotTo(HaveOccurred())
Expect(d).To(Equal(data{}))

res := []interface{}{"string", "str!",
"byte", "bytes!",
"int", "123",
"uint", "456",
"float", "123.456",
"bool", "1"}
Expect(Scan(res, &d)).NotTo(HaveOccurred())
Expect(d).To(Equal(data{
String: "str!",
Bytes: []byte("bytes!"),
Int: 123,
Uint: 456,
Float: 123.456,
Bool: true,
}))

// Scan a different type with the same values to test that
// the struct spec maps don't conflict.
type data2 struct {
String string `redis:"string"`
Bytes []byte `redis:"byte"`
Int int `redis:"int"`
Uint uint `redis:"uint"`
Float float32 `redis:"float"`
Bool bool `redis:"bool"`
}
var d2 data2
Expect(Scan(res, &d2)).NotTo(HaveOccurred())
Expect(d2).To(Equal(data2{
String: "str!",
Bytes: []byte("bytes!"),
Int: 123,
Uint: 456,
Float: 123.456,
Bool: true,
}))

Expect(Scan([]interface{}{
"string", "",
"float", "1",
"bool", "t"}, &d)).NotTo(HaveOccurred())
Expect(d).To(Equal(data{
String: "",
Bytes: []byte("bytes!"),
Int: 123,
Uint: 456,
Float: 1.0,
Bool: true,
}))
})

It("omits untagged fields", func() {
var d data

Expect(Scan([]interface{}{
"empty", "value",
"omit", "value",
"string", "str!"}, &d)).NotTo(HaveOccurred())
Expect(d).To(Equal(data{
String: "str!",
}))
})

It("catches bad values", func() {
var d data

Expect(Scan([]interface{}{"int", "a"}, &d)).To(HaveOccurred())
Expect(Scan([]interface{}{"uint", "a"}, &d)).To(HaveOccurred())
Expect(Scan([]interface{}{"uint", ""}, &d)).To(HaveOccurred())
Expect(Scan([]interface{}{"float", "b"}, &d)).To(HaveOccurred())
Expect(Scan([]interface{}{"bool", "-1"}, &d)).To(HaveOccurred())
Expect(Scan([]interface{}{"bool", ""}, &d)).To(HaveOccurred())
Expect(Scan([]interface{}{"bool", "123"}, &d)).To(HaveOccurred())
})
})
2 changes: 1 addition & 1 deletion internal/hscan/structmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func getStructFields(t reflect.Type, fieldTag string) *structFields {
for i := 0; i < num; i++ {
f := t.Field(i)

tag := t.Field(i).Tag.Get(fieldTag)
tag := f.Tag.Get(fieldTag)
if tag == "" || tag == "-" {
continue
}
Expand Down

0 comments on commit a4144ea

Please sign in to comment.