Skip to content
This repository has been archived by the owner on May 1, 2020. It is now read-only.

Commit

Permalink
fix(scan): Support Scan into pointer to RedisScan
Browse files Browse the repository at this point in the history
Add support for scanning into a pointer to a type which supports RedisScan.

Fixes gomodule#418
  • Loading branch information
stevenh committed Aug 22, 2019
1 parent 574c33c commit d3876d4
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 0 deletions.
20 changes: 20 additions & 0 deletions redis/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,26 @@ func convertAssignBulkString(d reflect.Value, s []byte) (err error) {
} else {
err = cannotConvert(d, s)
}
case reflect.Ptr:
if d.CanInterface() && d.CanSet() {
if s == nil {
if d.IsNil() {
return nil
}

d.Set(reflect.Zero(d.Type()))
return nil
}

if d.IsNil() {
d.Set(reflect.New(d.Type().Elem()))
}

if sc, ok := d.Interface().(Scanner); ok {
return sc.RedisScan(s)
}
}
err = convertAssignString(d, string(s))
default:
err = convertAssignString(d, string(s))
}
Expand Down
64 changes: 64 additions & 0 deletions redis/scan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ import (
"fmt"
"math"
"reflect"
"strconv"
"testing"
"time"

"github.com/gomodule/redigo/redis"
"github.com/stretchr/testify/require"
)

type durationScan struct {
Expand Down Expand Up @@ -457,6 +459,68 @@ func TestArgs(t *testing.T) {
}
}

type InnerStruct struct {
Foo int64
}

func (f *InnerStruct) RedisScan(src interface{}) (err error) {
switch s := src.(type) {
case []byte:
f.Foo, err = strconv.ParseInt(string(s), 10, 64)
case string:
f.Foo, err = strconv.ParseInt(s, 10, 64)
default:
return fmt.Errorf("invalid type %T", src)
}
return err
}

type OuterStruct struct {
Inner *InnerStruct
}

func TestScanPtrRedisScan(t *testing.T) {
tests := []struct {
name string
src []interface{}
dest OuterStruct
expected OuterStruct
}{
{
name: "value-to-nil",
src: []interface{}{[]byte("1234"), nil},
dest: OuterStruct{&InnerStruct{}},
expected: OuterStruct{Inner: &InnerStruct{Foo: 1234}},
},
{
name: "nil-to-nil",
src: []interface{}{[]byte(nil), nil},
dest: OuterStruct{},
expected: OuterStruct{},
},
{
name: "value-to-value",
src: []interface{}{[]byte("1234"), nil},
dest: OuterStruct{Inner: &InnerStruct{Foo: 5678}},
expected: OuterStruct{Inner: &InnerStruct{Foo: 1234}},
},
{
name: "nil-to-value",
src: []interface{}{[]byte(nil), nil},
dest: OuterStruct{Inner: &InnerStruct{Foo: 1234}},
expected: OuterStruct{},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
_, err := redis.Scan(tc.src, &tc.dest.Inner)
require.NoError(t, err)
require.Equal(t, tc.expected, tc.dest)
})
}
}

func ExampleArgs() {
c, err := dial()
if err != nil {
Expand Down

0 comments on commit d3876d4

Please sign in to comment.