diff --git a/command.go b/command.go index dc13e82d..e28eb758 100644 --- a/command.go +++ b/command.go @@ -542,6 +542,10 @@ func (cmd *StringSliceCmd) String() string { return cmdString(cmd, cmd.val) } +func (cmd *StringSliceCmd) ScanSlice(container interface{}) error { + return proto.ScanSlice(cmd.Val(), container) +} + func (cmd *StringSliceCmd) readReply(cn *pool.Conn) error { var v interface{} v, cmd.err = cn.Rd.ReadArrayReply(stringSliceParser) diff --git a/commands_test.go b/commands_test.go index 66359d2a..a524aafc 100644 --- a/commands_test.go +++ b/commands_test.go @@ -250,11 +250,11 @@ var _ = Describe("Commands", func() { Expect(exists.Err()).NotTo(HaveOccurred()) Expect(exists.Val()).To(Equal(false)) - existsMul := client.ExistsMulti("key1", "key2") + existsMul := client.ExistsMulti("key1", "key2") Expect(existsMul.Err()).NotTo(HaveOccurred()) Expect(existsMul.Val()).To(Equal(int64(1))) - existsMul = client.ExistsMulti("key1", "key1") + existsMul = client.ExistsMulti("key1", "key1") Expect(existsMul.Err()).NotTo(HaveOccurred()) Expect(existsMul.Val()).To(Equal(int64(2))) }) @@ -1264,14 +1264,19 @@ var _ = Describe("Commands", func() { }) It("should HVals", func() { - hSet := client.HSet("hash", "key1", "hello1") - Expect(hSet.Err()).NotTo(HaveOccurred()) - hSet = client.HSet("hash", "key2", "hello2") - Expect(hSet.Err()).NotTo(HaveOccurred()) + err := client.HSet("hash", "key1", "hello1").Err() + Expect(err).NotTo(HaveOccurred()) + err = client.HSet("hash", "key2", "hello2").Err() + Expect(err).NotTo(HaveOccurred()) - hVals := client.HVals("hash") - Expect(hVals.Err()).NotTo(HaveOccurred()) - Expect(hVals.Val()).To(Equal([]string{"hello1", "hello2"})) + v, err := client.HVals("hash").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(v).To(Equal([]string{"hello1", "hello2"})) + + var slice []string + err = client.HVals("hash").ScanSlice(&slice) + Expect(err).NotTo(HaveOccurred()) + Expect(slice).To(Equal([]string{"hello1", "hello2"})) }) }) diff --git a/internal/proto/scan.go b/internal/proto/scan.go index 67ea521c..f3c75d90 100644 --- a/internal/proto/scan.go +++ b/internal/proto/scan.go @@ -3,6 +3,7 @@ package proto import ( "encoding" "fmt" + "reflect" "gopkg.in/redis.v5/internal" ) @@ -105,3 +106,26 @@ func Scan(b []byte, v interface{}) error { "redis: can't unmarshal %T (consider implementing BinaryUnmarshaler)", v) } } + +func ScanSlice(data []string, slice interface{}) error { + v := reflect.ValueOf(slice) + if !v.IsValid() { + return fmt.Errorf("redis: ScanSlice(nil)") + } + if v.Kind() != reflect.Ptr { + return fmt.Errorf("redis: ScanSlice(non-pointer %T)", slice) + } + v = v.Elem() + if v.Kind() != reflect.Slice { + return fmt.Errorf("redis: ScanSlice(non-slice %T)", slice) + } + + for i, s := range data { + elem := internal.SliceNextElem(v) + if err := Scan([]byte(s), elem.Addr().Interface()); err != nil { + return fmt.Errorf("redis: ScanSlice(index=%d value=%q) failed: %s", i, s, err) + } + } + + return nil +} diff --git a/internal/proto/scan_test.go b/internal/proto/scan_test.go new file mode 100644 index 00000000..fadcd056 --- /dev/null +++ b/internal/proto/scan_test.go @@ -0,0 +1,48 @@ +package proto + +import ( + "encoding/json" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +type testScanSliceStruct struct { + ID int + Name string +} + +func (s *testScanSliceStruct) MarshalBinary() ([]byte, error) { + return json.Marshal(s) +} + +func (s *testScanSliceStruct) UnmarshalBinary(b []byte) error { + return json.Unmarshal(b, s) +} + +var _ = Describe("ScanSlice", func() { + data := []string{ + `{"ID":-1,"Name":"Back Yu"}`, + `{"ID":1,"Name":"szyhf"}`, + } + + It("[]testScanSliceStruct", func() { + var slice []testScanSliceStruct + err := ScanSlice(data, &slice) + Expect(err).NotTo(HaveOccurred()) + Expect(slice).To(Equal([]testScanSliceStruct{ + {-1, "Back Yu"}, + {1, "szyhf"}, + })) + }) + + It("var testContainer []*testScanSliceStruct", func() { + var slice []*testScanSliceStruct + err := ScanSlice(data, &slice) + Expect(err).NotTo(HaveOccurred()) + Expect(slice).To(Equal([]*testScanSliceStruct{ + {-1, "Back Yu"}, + {1, "szyhf"}, + })) + }) +}) diff --git a/internal/util.go b/internal/util.go index 06623383..520596fd 100644 --- a/internal/util.go +++ b/internal/util.go @@ -1,5 +1,7 @@ package internal +import "reflect" + func ToLower(s string) string { if isLower(s) { return s @@ -25,3 +27,21 @@ func isLower(s string) bool { } return true } + +func SliceNextElem(v reflect.Value) reflect.Value { + if v.Len() < v.Cap() { + v.Set(v.Slice(0, v.Len()+1)) + return v.Index(v.Len() - 1) + } + + elemType := v.Type().Elem() + + if elemType.Kind() == reflect.Ptr { + elem := reflect.New(elemType.Elem()) + v.Set(reflect.Append(v, elem)) + return elem.Elem() + } + + v.Set(reflect.Append(v, reflect.Zero(elemType))) + return v.Index(v.Len() - 1) +}