瀏覽代碼

Add Float64 and Strings reply helpers.

Gary Burd 12 年之前
父節點
當前提交
4f6a4ebc3b
共有 5 個文件被更改,包括 137 次插入12 次删除
  1. 8 2
      redis/conn.go
  2. 4 0
      redis/conn_test.go
  3. 6 5
      redis/doc.go
  4. 58 5
      redis/reply.go
  5. 61 0
      redis/reply_test.go

+ 8 - 2
redis/conn.go

@@ -48,7 +48,7 @@ type conn struct {
 	lenScratch [1 + 19 + 2]byte
 
 	// Scratch space for formatting integers.
-	intScratch [20]byte
+	numScratch [40]byte
 }
 
 // Dial connects to the Redis server at the given network and address.
@@ -136,7 +136,11 @@ func (c *conn) writeBytes(p []byte) error {
 }
 
 func (c *conn) writeInt64(n int64) error {
-	return c.writeBytes(strconv.AppendInt(c.intScratch[0:0], n, 10))
+	return c.writeBytes(strconv.AppendInt(c.numScratch[:0], n, 10))
+}
+
+func (c *conn) writeFloat64(n float64) error {
+	return c.writeBytes(strconv.AppendFloat(c.numScratch[:0], n, 'g', -1, 64))
 }
 
 func (c *conn) writeCommand(cmd string, args []interface{}) (err error) {
@@ -155,6 +159,8 @@ func (c *conn) writeCommand(cmd string, args []interface{}) (err error) {
 			err = c.writeInt64(int64(arg))
 		case int64:
 			err = c.writeInt64(arg)
+		case float64:
+			err = c.writeFloat64(arg)
 		case bool:
 			if arg {
 				err = c.writeString("1")

+ 4 - 0
redis/conn_test.go

@@ -52,6 +52,10 @@ var writeTests = []struct {
 		[]interface{}{"SET", "foo", int64(math.MinInt64)},
 		"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$20\r\n-9223372036854775808\r\n",
 	},
+	{
+		[]interface{}{"SET", "foo", float64(1349673917.939762)},
+		"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$21\r\n1.349673917939762e+09\r\n",
+	},
 	{
 		[]interface{}{"SET", "", []byte("foo")},
 		"*3\r\n$3\r\nSET\r\n$0\r\n\r\n$3\r\nfoo\r\n",

+ 6 - 5
redis/doc.go

@@ -123,11 +123,12 @@
 //
 // Reply Helpers
 //
-// The Bool, Int, Bytes, String and Values functions convert a reply to a value
-// of a specific type. To allow convenient wrapping of calls to the connection
-// Do and Receive methods, the functions take a second argument of type error.
-// If the error is non-nil, then the helper function returns the error. If the
-// error is nil, the function converts the reply to the specified type:
+// The Bool, Int, Bytes, String, Strings and Values functions convert a reply
+// to a value of a specific type. To allow convenient wrapping of calls to the
+// connection Do and Receive methods, the functions take a second argument of
+// type error.  If the error is non-nil, then the helper function returns the
+// error. If the error is nil, the function converts the reply to the specified
+// type:
 //
 //  exists, err := redis.Bool(c.Do("EXISTS", "foo"))
 //  if err != nil {

+ 58 - 5
redis/reply.go

@@ -28,7 +28,7 @@ var ErrNil = errors.New("redigo: nil returned")
 //
 //  Reply type    Result
 //  integer       int(reply), nil
-//  bulk          strconv.ParseInt(reply, 10, 0)
+//  bulk          parsed reply, nil
 //  nil           0, ErrNil
 //  other         0, error
 func Int(reply interface{}, err error) (int, error) {
@@ -54,12 +54,12 @@ func Int(reply interface{}, err error) (int, error) {
 }
 
 // Int64 is a helper that converts a command reply to 64 bit integer. If err is
-// not equal to nil, then Int returns 0, err. Otherwise, Int converts the reply
-// to an int as follows:
+// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the
+// reply to an int64 as follows:
 //
 //  Reply type    Result
 //  integer       reply, nil
-//  bulk          strconv.ParseInt(reply, 10, 64)
+//  bulk          parsed reply, nil
 //  nil           0, ErrNil
 //  other         0, error
 func Int64(reply interface{}, err error) (int64, error) {
@@ -80,6 +80,30 @@ func Int64(reply interface{}, err error) (int64, error) {
 	return 0, fmt.Errorf("redigo: unexpected type for Int64, got type %T", reply)
 }
 
+// Float64 is a helper that converts a command reply to 64 bit float. If err is
+// not equal to nil, then Float64 returns 0, err. Otherwise, Float64 converts
+// the reply to an int as follows:
+//
+//  Reply type    Result
+//  bulk          parsed reply, nil
+//  nil           0, ErrNil
+//  other         0, error
+func Float64(reply interface{}, err error) (float64, error) {
+	if err != nil {
+		return 0, err
+	}
+	switch reply := reply.(type) {
+	case []byte:
+		n, err := strconv.ParseFloat(string(reply), 64)
+		return n, err
+	case nil:
+		return 0, ErrNil
+	case Error:
+		return 0, reply
+	}
+	return 0, fmt.Errorf("redigo: unexpected type for Float64, got type %T", reply)
+}
+
 // String is a helper that converts a command reply to a string. If err is not
 // equal to nil, then String returns "", err. Otherwise String converts the
 // reply to a string as follows:
@@ -181,5 +205,34 @@ func Values(reply interface{}, err error) ([]interface{}, error) {
 	case Error:
 		return nil, reply
 	}
-	return nil, fmt.Errorf("redigo: unexpected type for Multi, got type %T", reply)
+	return nil, fmt.Errorf("redigo: unexpected type for Values, got type %T", reply)
+}
+
+// Strings is a helper that converts a multi-bulk command reply to a []string.
+// If err is not equal to nil, then Strings returns nil, err.  If one if the
+// multi-bulk items is not a bulk value or nil, then Strings returns an error.
+func Strings(reply interface{}, err error) ([]string, error) {
+	if err != nil {
+		return nil, err
+	}
+	switch reply := reply.(type) {
+	case []interface{}:
+		result := make([]string, len(reply))
+		for i := range reply {
+			if reply[i] == nil {
+				continue
+			}
+			p, ok := reply[i].([]byte)
+			if !ok {
+				return nil, fmt.Errorf("redigo: unexpected element type for Strings, got type %T", reply[i])
+			}
+			result[i] = string(p)
+		}
+		return result, nil
+	case nil:
+		return nil, ErrNil
+	case Error:
+		return nil, reply
+	}
+	return nil, fmt.Errorf("redigo: unexpected type for Strings, got type %T", reply)
 }

+ 61 - 0
redis/reply_test.go

@@ -16,9 +16,70 @@ package redis_test
 
 import (
 	"fmt"
+	"reflect"
+	"testing"
+
 	"github.com/garyburd/redigo/redis"
 )
 
+type valueError struct {
+	v   interface{}
+	err error
+}
+
+func ve(v interface{}, err error) valueError {
+	return valueError{v, err}
+}
+
+var replyTests = []struct {
+	name     interface{}
+	actual   valueError
+	expected valueError
+}{
+	{
+		"strings([v1, v2])",
+		ve(redis.Strings([]interface{}{[]byte("v1"), []byte("v2")}, nil)),
+		ve([]string{"v1", "v2"}, nil),
+	},
+	{
+		"strings(nil)",
+		ve(redis.Strings(nil, nil)),
+		ve([]string(nil), redis.ErrNil),
+	},
+	{
+		"values([v1, v2])",
+		ve(redis.Values([]interface{}{[]byte("v1"), []byte("v2")}, nil)),
+		ve([]interface{}{[]byte("v1"), []byte("v2")}, nil),
+	},
+	{
+		"values(nil)",
+		ve(redis.Values(nil, nil)),
+		ve([]interface{}(nil), redis.ErrNil),
+	},
+	{
+		"float64(1.0)",
+		ve(redis.Float64([]byte("1.0"), nil)),
+		ve(float64(1.0), nil),
+	},
+	{
+		"float64(nil)",
+		ve(redis.Float64(nil, nil)),
+		ve(float64(0.0), redis.ErrNil),
+	},
+}
+
+func TestReply(t *testing.T) {
+	for _, rt := range replyTests {
+		if rt.actual.err != rt.expected.err {
+			t.Errorf("%s returned err %v, want %v", rt.name, rt.actual.err, rt.expected.err)
+			continue
+		}
+		if !reflect.DeepEqual(rt.actual.v, rt.expected.v) {
+			t.Errorf("%s=%+v, want %+v", rt.name, rt.actual.v, rt.expected.v)
+		}
+	}
+}
+
 func ExampleBool() {
 	c, err := dial()
 	if err != nil {