fix: surface underlying error for slice / map helpers (#580)
Surface the underlying error when processing results for slice and map
helpers so that the user can see the real cause and not a type mismatch
error.
Also:
* Formatting changes to improve error case separation.
* Leverage %w in reply errors.
Fixes: #579
diff --git a/redis/reply.go b/redis/reply.go
index dfe6aff..20232e7 100644
--- a/redis/reply.go
+++ b/redis/reply.go
@@ -277,13 +277,16 @@
func Float64s(reply interface{}, err error) ([]float64, error) {
var result []float64
err = sliceHelper(reply, err, "Float64s", func(n int) { result = make([]float64, n) }, func(i int, v interface{}) error {
- p, ok := v.([]byte)
- if !ok {
- return fmt.Errorf("redigo: unexpected element type for Floats64, got type %T", v)
+ switch v := v.(type) {
+ case []byte:
+ f, err := strconv.ParseFloat(string(v), 64)
+ result[i] = f
+ return err
+ case Error:
+ return v
+ default:
+ return fmt.Errorf("redigo: unexpected element type for Float64s, got type %T", v)
}
- f, err := strconv.ParseFloat(string(p), 64)
- result[i] = f
- return err
})
return result, err
}
@@ -302,6 +305,8 @@
case []byte:
result[i] = string(v)
return nil
+ case Error:
+ return v
default:
return fmt.Errorf("redigo: unexpected element type for Strings, got type %T", v)
}
@@ -316,12 +321,15 @@
func ByteSlices(reply interface{}, err error) ([][]byte, error) {
var result [][]byte
err = sliceHelper(reply, err, "ByteSlices", func(n int) { result = make([][]byte, n) }, func(i int, v interface{}) error {
- p, ok := v.([]byte)
- if !ok {
+ switch v := v.(type) {
+ case []byte:
+ result[i] = v
+ return nil
+ case Error:
+ return v
+ default:
return fmt.Errorf("redigo: unexpected element type for ByteSlices, got type %T", v)
}
- result[i] = p
- return nil
})
return result, err
}
@@ -341,6 +349,8 @@
n, err := strconv.ParseInt(string(v), 10, 64)
result[i] = n
return err
+ case Error:
+ return v
default:
return fmt.Errorf("redigo: unexpected element type for Int64s, got type %T", v)
}
@@ -367,6 +377,8 @@
n, err := strconv.Atoi(string(v))
result[i] = n
return err
+ case Error:
+ return v
default:
return fmt.Errorf("redigo: unexpected element type for Ints, got type %T", v)
}
@@ -382,16 +394,23 @@
if err != nil {
return nil, err
}
+
if len(values)%2 != 0 {
- return nil, errors.New("redigo: StringMap expects even number of values result")
+ return nil, fmt.Errorf("redigo: StringMap expects even number of values result, got %d", len(values))
}
+
m := make(map[string]string, len(values)/2)
for i := 0; i < len(values); i += 2 {
- key, okKey := values[i].([]byte)
- value, okValue := values[i+1].([]byte)
- if !okKey || !okValue {
- return nil, errors.New("redigo: StringMap key not a bulk string value")
+ key, ok := values[i].([]byte)
+ if !ok {
+ return nil, fmt.Errorf("redigo: StringMap key[%d] not a bulk string value, got %T", i, values[i])
}
+
+ value, ok := values[i+1].([]byte)
+ if !ok {
+ return nil, fmt.Errorf("redigo: StringMap value[%d] not a bulk string value, got %T", i+1, values[i+1])
+ }
+
m[string(key)] = string(value)
}
return m, nil
@@ -405,19 +424,23 @@
if err != nil {
return nil, err
}
+
if len(values)%2 != 0 {
- return nil, errors.New("redigo: IntMap expects even number of values result")
+ return nil, fmt.Errorf("redigo: IntMap expects even number of values result, got %d", len(values))
}
+
m := make(map[string]int, len(values)/2)
for i := 0; i < len(values); i += 2 {
key, ok := values[i].([]byte)
if !ok {
- return nil, errors.New("redigo: IntMap key not a bulk string value")
+ return nil, fmt.Errorf("redigo: IntMap key[%d] not a bulk string value, got %T", i, values[i])
}
+
value, err := Int(values[i+1], nil)
if err != nil {
return nil, err
}
+
m[string(key)] = value
}
return m, nil
@@ -431,19 +454,23 @@
if err != nil {
return nil, err
}
+
if len(values)%2 != 0 {
- return nil, errors.New("redigo: Int64Map expects even number of values result")
+ return nil, fmt.Errorf("redigo: Int64Map expects even number of values result, got %d", len(values))
}
+
m := make(map[string]int64, len(values)/2)
for i := 0; i < len(values); i += 2 {
key, ok := values[i].([]byte)
if !ok {
- return nil, errors.New("redigo: Int64Map key not a bulk string value")
+ return nil, fmt.Errorf("redigo: Int64Map key[%d] not a bulk string value, got %T", i, values[i])
}
+
value, err := Int64(values[i+1], nil)
if err != nil {
return nil, err
}
+
m[string(key)] = value
}
return m, nil
@@ -461,21 +488,26 @@
if values[i] == nil {
continue
}
+
p, ok := values[i].([]interface{})
if !ok {
return nil, fmt.Errorf("redigo: unexpected element type for interface slice, got type %T", values[i])
}
+
if len(p) != 2 {
return nil, fmt.Errorf("redigo: unexpected number of values for a member position, got %d", len(p))
}
+
lat, err := Float64(p[0], nil)
if err != nil {
return nil, err
}
+
long, err := Float64(p[1], nil)
if err != nil {
return nil, err
}
+
positions[i] = &[2]float64{lat, long}
}
return positions, nil
@@ -496,6 +528,8 @@
n, err := strconv.ParseUint(string(v), 10, 64)
result[i] = n
return err
+ case Error:
+ return v
default:
return fmt.Errorf("redigo: unexpected element type for Uint64s, got type %T", v)
}
@@ -512,18 +546,20 @@
return nil, err
}
if len(values)%2 != 0 {
- return nil, errors.New("redigo: Uint64Map expects even number of values result")
+ return nil, fmt.Errorf("redigo: Uint64Map expects even number of values result, got %d", len(values))
}
m := make(map[string]uint64, len(values)/2)
for i := 0; i < len(values); i += 2 {
key, ok := values[i].([]byte)
if !ok {
- return nil, errors.New("redigo: Uint64Map key not a bulk string value")
+ return nil, fmt.Errorf("redigo: Uint64Map key[%d] not a bulk string value, got %T", i, values[i])
}
+
value, err := Uint64(values[i+1], nil)
if err != nil {
return nil, err
}
+
m[string(key)] = value
}
return m, nil
@@ -537,44 +573,49 @@
return nil, err
}
logs := make([]SlowLog, len(rawLogs))
- for i, rawLog := range rawLogs {
- rawLog, ok := rawLog.([]interface{})
+ for i, e := range rawLogs {
+ rawLog, ok := e.([]interface{})
if !ok {
- return nil, errors.New("redigo: slowlog element is not an array")
+ return nil, fmt.Errorf("redigo: slowlog element is not an array, got %T", e)
}
var log SlowLog
-
if len(rawLog) < 4 {
- return nil, errors.New("redigo: slowlog element has less than four elements")
+ return nil, fmt.Errorf("redigo: slowlog element has %d elements, expected at least 4", len(rawLog))
}
+
log.ID, ok = rawLog[0].(int64)
if !ok {
- return nil, errors.New("redigo: slowlog element[0] not an int64")
+ return nil, fmt.Errorf("redigo: slowlog element[0] not an int64, got %T", rawLog[0])
}
+
timestamp, ok := rawLog[1].(int64)
if !ok {
- return nil, errors.New("redigo: slowlog element[1] not an int64")
+ return nil, fmt.Errorf("redigo: slowlog element[1] not an int64, got %T", rawLog[0])
}
+
log.Time = time.Unix(timestamp, 0)
duration, ok := rawLog[2].(int64)
if !ok {
- return nil, errors.New("redigo: slowlog element[2] not an int64")
+ return nil, fmt.Errorf("redigo: slowlog element[2] not an int64, got %T", rawLog[0])
}
+
log.ExecutionTime = time.Duration(duration) * time.Microsecond
log.Args, err = Strings(rawLog[3], nil)
if err != nil {
- return nil, fmt.Errorf("redigo: slowlog element[3] is not array of string. actual error is : %s", err.Error())
+ return nil, fmt.Errorf("redigo: slowlog element[3] is not array of strings: %w", err)
}
+
if len(rawLog) >= 6 {
log.ClientAddr, err = String(rawLog[4], nil)
if err != nil {
- return nil, fmt.Errorf("redigo: slowlog element[4] is not a string. actual error is : %s", err.Error())
+ return nil, fmt.Errorf("redigo: slowlog element[4] is not a string: %w", err)
}
+
log.ClientName, err = String(rawLog[5], nil)
if err != nil {
- return nil, fmt.Errorf("redigo: slowlog element[5] is not a string. actual error is : %s", err.Error())
+ return nil, fmt.Errorf("redigo: slowlog element[5] is not a string: %w", err)
}
}
logs[i] = log