mirror of
https://github.com/ipfs/kubo.git
synced 2025-06-28 08:47:42 +08:00
Merge pull request #1427 from ipfs/unused-cleanup
cleanup unused packages
This commit is contained in:
5
Godeps/Godeps.json
generated
5
Godeps/Godeps.json
generated
@ -87,11 +87,6 @@
|
|||||||
"ImportPath": "github.com/fd/go-nat",
|
"ImportPath": "github.com/fd/go-nat",
|
||||||
"Rev": "50e7633d5f27d81490026a13e5b92d2e42d8c6bb"
|
"Rev": "50e7633d5f27d81490026a13e5b92d2e42d8c6bb"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"ImportPath": "github.com/fzzy/radix/redis",
|
|
||||||
"Comment": "v0.5.1",
|
|
||||||
"Rev": "27a863cdffdb0998d13e1e11992b18489aeeaa25"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/gogo/protobuf/io",
|
"ImportPath": "github.com/gogo/protobuf/io",
|
||||||
"Rev": "0ac967c269268f1af7d9bcc7927ccc9a589b2b36"
|
"Rev": "0ac967c269268f1af7d9bcc7927ccc9a589b2b36"
|
||||||
|
244
Godeps/_workspace/src/github.com/fzzy/radix/redis/client.go
generated
vendored
244
Godeps/_workspace/src/github.com/fzzy/radix/redis/client.go
generated
vendored
@ -1,244 +0,0 @@
|
|||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/fzzy/radix/redis/resp"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
bufSize int = 4096
|
|
||||||
)
|
|
||||||
|
|
||||||
//* Common errors
|
|
||||||
|
|
||||||
var LoadingError error = errors.New("server is busy loading dataset in memory")
|
|
||||||
var PipelineQueueEmptyError error = errors.New("pipeline queue empty")
|
|
||||||
|
|
||||||
//* Client
|
|
||||||
|
|
||||||
// Client describes a Redis client.
|
|
||||||
type Client struct {
|
|
||||||
// The connection the client talks to redis over. Don't touch this unless
|
|
||||||
// you know what you're doing.
|
|
||||||
Conn net.Conn
|
|
||||||
timeout time.Duration
|
|
||||||
reader *bufio.Reader
|
|
||||||
pending []*request
|
|
||||||
completed []*Reply
|
|
||||||
}
|
|
||||||
|
|
||||||
// request describes a client's request to the redis server
|
|
||||||
type request struct {
|
|
||||||
cmd string
|
|
||||||
args []interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial connects to the given Redis server with the given timeout, which will be
|
|
||||||
// used as the read/write timeout when communicating with redis
|
|
||||||
func DialTimeout(network, addr string, timeout time.Duration) (*Client, error) {
|
|
||||||
// establish a connection
|
|
||||||
conn, err := net.Dial(network, addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c := new(Client)
|
|
||||||
c.Conn = conn
|
|
||||||
c.timeout = timeout
|
|
||||||
c.reader = bufio.NewReaderSize(conn, bufSize)
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial connects to the given Redis server.
|
|
||||||
func Dial(network, addr string) (*Client, error) {
|
|
||||||
return DialTimeout(network, addr, time.Duration(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
//* Public methods
|
|
||||||
|
|
||||||
// Close closes the connection.
|
|
||||||
func (c *Client) Close() error {
|
|
||||||
return c.Conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cmd calls the given Redis command.
|
|
||||||
func (c *Client) Cmd(cmd string, args ...interface{}) *Reply {
|
|
||||||
err := c.writeRequest(&request{cmd, args})
|
|
||||||
if err != nil {
|
|
||||||
return &Reply{Type: ErrorReply, Err: err}
|
|
||||||
}
|
|
||||||
return c.ReadReply()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append adds the given call to the pipeline queue.
|
|
||||||
// Use GetReply() to read the reply.
|
|
||||||
func (c *Client) Append(cmd string, args ...interface{}) {
|
|
||||||
c.pending = append(c.pending, &request{cmd, args})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetReply returns the reply for the next request in the pipeline queue.
|
|
||||||
// Error reply with PipelineQueueEmptyError is returned,
|
|
||||||
// if the pipeline queue is empty.
|
|
||||||
func (c *Client) GetReply() *Reply {
|
|
||||||
if len(c.completed) > 0 {
|
|
||||||
r := c.completed[0]
|
|
||||||
c.completed = c.completed[1:]
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
c.completed = nil
|
|
||||||
|
|
||||||
if len(c.pending) == 0 {
|
|
||||||
return &Reply{Type: ErrorReply, Err: PipelineQueueEmptyError}
|
|
||||||
}
|
|
||||||
|
|
||||||
nreqs := len(c.pending)
|
|
||||||
err := c.writeRequest(c.pending...)
|
|
||||||
c.pending = nil
|
|
||||||
if err != nil {
|
|
||||||
return &Reply{Type: ErrorReply, Err: err}
|
|
||||||
}
|
|
||||||
r := c.ReadReply()
|
|
||||||
c.completed = make([]*Reply, nreqs-1)
|
|
||||||
for i := 0; i < nreqs-1; i++ {
|
|
||||||
c.completed[i] = c.ReadReply()
|
|
||||||
}
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
//* Private methods
|
|
||||||
|
|
||||||
func (c *Client) setReadTimeout() {
|
|
||||||
if c.timeout != 0 {
|
|
||||||
c.Conn.SetReadDeadline(time.Now().Add(c.timeout))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) setWriteTimeout() {
|
|
||||||
if c.timeout != 0 {
|
|
||||||
c.Conn.SetWriteDeadline(time.Now().Add(c.timeout))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will read a redis reply off of the connection without sending anything
|
|
||||||
// first (useful after you've sent a SUSBSCRIBE command). This will block until
|
|
||||||
// a reply is received or the timeout is reached. On timeout an ErrorReply will
|
|
||||||
// be returned, you can check if it's a timeout like so:
|
|
||||||
//
|
|
||||||
// r := conn.ReadReply()
|
|
||||||
// if r.Err != nil {
|
|
||||||
// if t, ok := r.Err.(*net.OpError); ok && t.Timeout() {
|
|
||||||
// // Is timeout
|
|
||||||
// } else {
|
|
||||||
// // Not timeout
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Note: this is a more low-level function, you really shouldn't have to
|
|
||||||
// actually use it unless you're writing your own pub/sub code
|
|
||||||
func (c *Client) ReadReply() *Reply {
|
|
||||||
c.setReadTimeout()
|
|
||||||
return c.parse()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) writeRequest(requests ...*request) error {
|
|
||||||
c.setWriteTimeout()
|
|
||||||
for i := range requests {
|
|
||||||
req := make([]interface{}, 0, len(requests[i].args)+1)
|
|
||||||
req = append(req, requests[i].cmd)
|
|
||||||
req = append(req, requests[i].args...)
|
|
||||||
err := resp.WriteArbitraryAsFlattenedStrings(c.Conn, req)
|
|
||||||
if err != nil {
|
|
||||||
c.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) parse() *Reply {
|
|
||||||
m, err := resp.ReadMessage(c.reader)
|
|
||||||
if err != nil {
|
|
||||||
if t, ok := err.(*net.OpError); !ok || !t.Timeout() {
|
|
||||||
// close connection except timeout
|
|
||||||
c.Close()
|
|
||||||
}
|
|
||||||
return &Reply{Type: ErrorReply, Err: err}
|
|
||||||
}
|
|
||||||
r, err := messageToReply(m)
|
|
||||||
if err != nil {
|
|
||||||
return &Reply{Type: ErrorReply, Err: err}
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// The error return parameter is for bubbling up parse errors and the like, if
|
|
||||||
// the error is sent by redis itself as an Err message type, then it will be
|
|
||||||
// sent back as an actual Reply (wrapped in a CmdError)
|
|
||||||
func messageToReply(m *resp.Message) (*Reply, error) {
|
|
||||||
r := &Reply{}
|
|
||||||
|
|
||||||
switch m.Type {
|
|
||||||
case resp.Err:
|
|
||||||
errMsg, err := m.Err()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(errMsg.Error(), "LOADING") {
|
|
||||||
err = LoadingError
|
|
||||||
} else {
|
|
||||||
err = &CmdError{errMsg}
|
|
||||||
}
|
|
||||||
r.Type = ErrorReply
|
|
||||||
r.Err = err
|
|
||||||
|
|
||||||
case resp.SimpleStr:
|
|
||||||
status, err := m.Bytes()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
r.Type = StatusReply
|
|
||||||
r.buf = status
|
|
||||||
|
|
||||||
case resp.Int:
|
|
||||||
i, err := m.Int()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
r.Type = IntegerReply
|
|
||||||
r.int = i
|
|
||||||
|
|
||||||
case resp.BulkStr:
|
|
||||||
b, err := m.Bytes()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
r.Type = BulkReply
|
|
||||||
r.buf = b
|
|
||||||
|
|
||||||
case resp.Nil:
|
|
||||||
r.Type = NilReply
|
|
||||||
|
|
||||||
case resp.Array:
|
|
||||||
ms, err := m.Array()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
r.Type = MultiReply
|
|
||||||
r.Elems = make([]*Reply, len(ms))
|
|
||||||
for i := range ms {
|
|
||||||
r.Elems[i], err = messageToReply(ms[i])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r, nil
|
|
||||||
|
|
||||||
}
|
|
106
Godeps/_workspace/src/github.com/fzzy/radix/redis/client_test.go
generated
vendored
106
Godeps/_workspace/src/github.com/fzzy/radix/redis/client_test.go
generated
vendored
@ -1,106 +0,0 @@
|
|||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
. "testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func dial(t *T) *Client {
|
|
||||||
client, err := DialTimeout("tcp", "127.0.0.1:6379", 10*time.Second)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCmd(t *T) {
|
|
||||||
c := dial(t)
|
|
||||||
v, _ := c.Cmd("echo", "Hello, World!").Str()
|
|
||||||
assert.Equal(t, "Hello, World!", v)
|
|
||||||
|
|
||||||
// Test that a bad command properly returns a *CmdError
|
|
||||||
err := c.Cmd("non-existant-cmd").Err
|
|
||||||
assert.NotEqual(t, "", err.(*CmdError).Error())
|
|
||||||
|
|
||||||
// Test that application level errors propagate correctly
|
|
||||||
c.Cmd("sadd", "foo", "bar")
|
|
||||||
_, err = c.Cmd("get", "foo").Str()
|
|
||||||
assert.NotEqual(t, "", err.(*CmdError).Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPipeline(t *T) {
|
|
||||||
c := dial(t)
|
|
||||||
c.Append("echo", "foo")
|
|
||||||
c.Append("echo", "bar")
|
|
||||||
c.Append("echo", "zot")
|
|
||||||
|
|
||||||
v, _ := c.GetReply().Str()
|
|
||||||
assert.Equal(t, "foo", v)
|
|
||||||
|
|
||||||
v, _ = c.GetReply().Str()
|
|
||||||
assert.Equal(t, "bar", v)
|
|
||||||
|
|
||||||
v, _ = c.GetReply().Str()
|
|
||||||
assert.Equal(t, "zot", v)
|
|
||||||
|
|
||||||
r := c.GetReply()
|
|
||||||
assert.Equal(t, ErrorReply, r.Type)
|
|
||||||
assert.Equal(t, PipelineQueueEmptyError, r.Err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParse(t *T) {
|
|
||||||
c := dial(t)
|
|
||||||
|
|
||||||
parseString := func(b string) *Reply {
|
|
||||||
c.reader = bufio.NewReader(bytes.NewBufferString(b))
|
|
||||||
return c.parse()
|
|
||||||
}
|
|
||||||
|
|
||||||
// missing \n trailing
|
|
||||||
r := parseString("foo")
|
|
||||||
assert.Equal(t, ErrorReply, r.Type)
|
|
||||||
assert.NotNil(t, r.Err)
|
|
||||||
|
|
||||||
// error reply
|
|
||||||
r = parseString("-ERR unknown command 'foobar'\r\n")
|
|
||||||
assert.Equal(t, ErrorReply, r.Type)
|
|
||||||
assert.Equal(t, "ERR unknown command 'foobar'", r.Err.Error())
|
|
||||||
|
|
||||||
// LOADING error
|
|
||||||
r = parseString("-LOADING Redis is loading the dataset in memory\r\n")
|
|
||||||
assert.Equal(t, ErrorReply, r.Type)
|
|
||||||
assert.Equal(t, LoadingError, r.Err)
|
|
||||||
|
|
||||||
// status reply
|
|
||||||
r = parseString("+OK\r\n")
|
|
||||||
assert.Equal(t, StatusReply, r.Type)
|
|
||||||
assert.Equal(t, []byte("OK"), r.buf)
|
|
||||||
|
|
||||||
// integer reply
|
|
||||||
r = parseString(":1337\r\n")
|
|
||||||
assert.Equal(t, IntegerReply, r.Type)
|
|
||||||
assert.Equal(t, int64(1337), r.int)
|
|
||||||
|
|
||||||
// null bulk reply
|
|
||||||
r = parseString("$-1\r\n")
|
|
||||||
assert.Equal(t, NilReply, r.Type)
|
|
||||||
|
|
||||||
// bulk reply
|
|
||||||
r = parseString("$6\r\nfoobar\r\n")
|
|
||||||
assert.Equal(t, BulkReply, r.Type)
|
|
||||||
assert.Equal(t, []byte("foobar"), r.buf)
|
|
||||||
|
|
||||||
// null multi bulk reply
|
|
||||||
r = parseString("*-1\r\n")
|
|
||||||
assert.Equal(t, NilReply, r.Type)
|
|
||||||
|
|
||||||
// multi bulk reply
|
|
||||||
r = parseString("*5\r\n:0\r\n:1\r\n:2\r\n:3\r\n$6\r\nfoobar\r\n")
|
|
||||||
assert.Equal(t, MultiReply, r.Type)
|
|
||||||
assert.Equal(t, 5, len(r.Elems))
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
assert.Equal(t, int64(i), r.Elems[i].int)
|
|
||||||
}
|
|
||||||
assert.Equal(t, []byte("foobar"), r.Elems[4].buf)
|
|
||||||
}
|
|
87
Godeps/_workspace/src/github.com/fzzy/radix/redis/doc.go
generated
vendored
87
Godeps/_workspace/src/github.com/fzzy/radix/redis/doc.go
generated
vendored
@ -1,87 +0,0 @@
|
|||||||
// A simple client for connecting and interacting with redis.
|
|
||||||
//
|
|
||||||
// To import inside your package do:
|
|
||||||
//
|
|
||||||
// import "github.com/fzzy/radix/redis"
|
|
||||||
//
|
|
||||||
// Connecting
|
|
||||||
//
|
|
||||||
// Use either Dial or DialTimeout:
|
|
||||||
//
|
|
||||||
// client, err := redis.Dial("tcp", "localhost:6379")
|
|
||||||
// if err != nil {
|
|
||||||
// // handle err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Make sure to call Close on the client if you want to clean it up before the
|
|
||||||
// end of the program.
|
|
||||||
//
|
|
||||||
// Cmd and Reply
|
|
||||||
//
|
|
||||||
// The Cmd method returns a Reply, which has methods for converting to various
|
|
||||||
// types. Each of these methods returns an error which can either be a
|
|
||||||
// connection error (e.g. timeout), an application error (e.g. key is wrong
|
|
||||||
// type), or a conversion error (e.g. cannot convert to integer). You can also
|
|
||||||
// directly check the error using the Err field:
|
|
||||||
//
|
|
||||||
// foo, err := client.Cmd("GET", "foo").Str()
|
|
||||||
// if err != nil {
|
|
||||||
// // handle err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Checking Err field directly
|
|
||||||
//
|
|
||||||
// err = client.Cmd("PING").Err
|
|
||||||
// if err != nil {
|
|
||||||
// // handle err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Multi Replies
|
|
||||||
//
|
|
||||||
// The elements to Multi replies can be accessed as strings using List or
|
|
||||||
// ListBytes, or you can use the Elems field for more low-level access:
|
|
||||||
//
|
|
||||||
// r := client.Cmd("MGET", "foo", "bar", "baz")
|
|
||||||
//
|
|
||||||
// // This:
|
|
||||||
// for _, elemStr := range r.List() {
|
|
||||||
// fmt.Println(elemStr)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // is equivalent to this:
|
|
||||||
// for i := range r.Elems {
|
|
||||||
// elemStr, _ := r.Elems[i].Str()
|
|
||||||
// fmt.Println(elemStr)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Pipelining
|
|
||||||
//
|
|
||||||
// Pipelining is when the client sends a bunch of commands to the server at
|
|
||||||
// once, and only once all the commands have been sent does it start reading the
|
|
||||||
// replies off the socket. This is supported using the Append and GetReply
|
|
||||||
// methods. Append will simply append the command to a buffer without sending
|
|
||||||
// it, the first time GetReply is called it will send all the commands in the
|
|
||||||
// buffer and return the Reply for the first command that was sent. Subsequent
|
|
||||||
// calls to GetReply return Replys for subsequent commands:
|
|
||||||
//
|
|
||||||
// client.Append("GET", "foo")
|
|
||||||
// client.Append("SET", "bar", "foo")
|
|
||||||
// client.Append("DEL", "baz")
|
|
||||||
//
|
|
||||||
// // Read GET foo reply
|
|
||||||
// foo, err := client.GetReply().Str()
|
|
||||||
// if err != nil {
|
|
||||||
// // handle err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Read SET bar foo reply
|
|
||||||
// if err := client.GetReply().Err; err != nil {
|
|
||||||
// // handle err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Read DEL baz reply
|
|
||||||
// if err := client.GetReply().Err; err != nil {
|
|
||||||
// // handle err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
package redis
|
|
275
Godeps/_workspace/src/github.com/fzzy/radix/redis/reply.go
generated
vendored
275
Godeps/_workspace/src/github.com/fzzy/radix/redis/reply.go
generated
vendored
@ -1,275 +0,0 @@
|
|||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A CmdError implements the error interface and is what is returned when the
|
|
||||||
// server returns an error on the application level (e.g. key doesn't exist or
|
|
||||||
// is the wrong type), as opposed to a connection/transport error.
|
|
||||||
//
|
|
||||||
// You can test if a reply is a CmdError like so:
|
|
||||||
//
|
|
||||||
// r := conn.Cmd("GET", "key-which-isnt-a-string")
|
|
||||||
// if r.Err != nil {
|
|
||||||
// if cerr, ok := r.Err.(*redis.CmdError); ok {
|
|
||||||
// // Is CmdError
|
|
||||||
// } else {
|
|
||||||
// // Is other error
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
type CmdError struct {
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cerr *CmdError) Error() string {
|
|
||||||
return cerr.Err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if error returned was due to the redis server being read only
|
|
||||||
func (cerr *CmdError) Readonly() bool {
|
|
||||||
return strings.HasPrefix(cerr.Err.Error(), "READONLY")
|
|
||||||
}
|
|
||||||
|
|
||||||
//* Reply
|
|
||||||
|
|
||||||
/*
|
|
||||||
ReplyType describes type of a reply.
|
|
||||||
|
|
||||||
Possible values are:
|
|
||||||
|
|
||||||
StatusReply -- status reply
|
|
||||||
ErrorReply -- error reply
|
|
||||||
IntegerReply -- integer reply
|
|
||||||
NilReply -- nil reply
|
|
||||||
BulkReply -- bulk reply
|
|
||||||
MultiReply -- multi bulk reply
|
|
||||||
*/
|
|
||||||
type ReplyType uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
StatusReply ReplyType = iota
|
|
||||||
ErrorReply
|
|
||||||
IntegerReply
|
|
||||||
NilReply
|
|
||||||
BulkReply
|
|
||||||
MultiReply
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reply holds a Redis reply.
|
|
||||||
type Reply struct {
|
|
||||||
Type ReplyType // Reply type
|
|
||||||
Elems []*Reply // Sub-replies
|
|
||||||
Err error // Reply error
|
|
||||||
buf []byte
|
|
||||||
int int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns the reply value as a byte string or
|
|
||||||
// an error, if the reply type is not StatusReply or BulkReply.
|
|
||||||
func (r *Reply) Bytes() ([]byte, error) {
|
|
||||||
if r.Type == ErrorReply {
|
|
||||||
return nil, r.Err
|
|
||||||
}
|
|
||||||
if !(r.Type == StatusReply || r.Type == BulkReply) {
|
|
||||||
return nil, errors.New("string value is not available for this reply type")
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.buf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Str is a convenience method for calling Reply.Bytes() and converting it to string
|
|
||||||
func (r *Reply) Str() (string, error) {
|
|
||||||
b, err := r.Bytes()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(b), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64 returns the reply value as a int64 or an error,
|
|
||||||
// if the reply type is not IntegerReply or the reply type
|
|
||||||
// BulkReply could not be parsed to an int64.
|
|
||||||
func (r *Reply) Int64() (int64, error) {
|
|
||||||
if r.Type == ErrorReply {
|
|
||||||
return 0, r.Err
|
|
||||||
}
|
|
||||||
if r.Type != IntegerReply {
|
|
||||||
s, err := r.Str()
|
|
||||||
if err == nil {
|
|
||||||
i64, err := strconv.ParseInt(s, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0, errors.New("failed to parse integer value from string value")
|
|
||||||
} else {
|
|
||||||
return i64, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, errors.New("integer value is not available for this reply type")
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.int, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int is a convenience method for calling Reply.Int64() and converting it to int.
|
|
||||||
func (r *Reply) Int() (int, error) {
|
|
||||||
i64, err := r.Int64()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return int(i64), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bool returns false, if the reply value equals to 0 or "0", otherwise true; or
|
|
||||||
// an error, if the reply type is not IntegerReply or BulkReply.
|
|
||||||
func (r *Reply) Bool() (bool, error) {
|
|
||||||
if r.Type == ErrorReply {
|
|
||||||
return false, r.Err
|
|
||||||
}
|
|
||||||
i, err := r.Int()
|
|
||||||
if err == nil {
|
|
||||||
if i == 0 {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err := r.Str()
|
|
||||||
if err == nil {
|
|
||||||
if s == "0" {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, errors.New("boolean value is not available for this reply type")
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns a multi bulk reply as a slice of strings or an error.
|
|
||||||
// The reply type must be MultiReply and its elements' types must all be either BulkReply or NilReply.
|
|
||||||
// Nil elements are returned as empty strings.
|
|
||||||
// Useful for list commands.
|
|
||||||
func (r *Reply) List() ([]string, error) {
|
|
||||||
// Doing all this in two places instead of just calling ListBytes() so we don't have
|
|
||||||
// to iterate twice
|
|
||||||
if r.Type == ErrorReply {
|
|
||||||
return nil, r.Err
|
|
||||||
}
|
|
||||||
if r.Type != MultiReply {
|
|
||||||
return nil, errors.New("reply type is not MultiReply")
|
|
||||||
}
|
|
||||||
|
|
||||||
strings := make([]string, len(r.Elems))
|
|
||||||
for i, v := range r.Elems {
|
|
||||||
if v.Type == BulkReply {
|
|
||||||
strings[i] = string(v.buf)
|
|
||||||
} else if v.Type == NilReply {
|
|
||||||
strings[i] = ""
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("element type is not BulkReply or NilReply")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListBytes returns a multi bulk reply as a slice of bytes slices or an error.
|
|
||||||
// The reply type must be MultiReply and its elements' types must all be either BulkReply or NilReply.
|
|
||||||
// Nil elements are returned as nil.
|
|
||||||
// Useful for list commands.
|
|
||||||
func (r *Reply) ListBytes() ([][]byte, error) {
|
|
||||||
if r.Type == ErrorReply {
|
|
||||||
return nil, r.Err
|
|
||||||
}
|
|
||||||
if r.Type != MultiReply {
|
|
||||||
return nil, errors.New("reply type is not MultiReply")
|
|
||||||
}
|
|
||||||
|
|
||||||
bufs := make([][]byte, len(r.Elems))
|
|
||||||
for i, v := range r.Elems {
|
|
||||||
if v.Type == BulkReply {
|
|
||||||
bufs[i] = v.buf
|
|
||||||
} else if v.Type == NilReply {
|
|
||||||
bufs[i] = nil
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("element type is not BulkReply or NilReply")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bufs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash returns a multi bulk reply as a map[string]string or an error.
|
|
||||||
// The reply type must be MultiReply,
|
|
||||||
// it must have an even number of elements,
|
|
||||||
// they must be in a "key value key value..." order and
|
|
||||||
// values must all be either BulkReply or NilReply.
|
|
||||||
// Nil values are returned as empty strings.
|
|
||||||
// Useful for hash commands.
|
|
||||||
func (r *Reply) Hash() (map[string]string, error) {
|
|
||||||
if r.Type == ErrorReply {
|
|
||||||
return nil, r.Err
|
|
||||||
}
|
|
||||||
rmap := map[string]string{}
|
|
||||||
|
|
||||||
if r.Type != MultiReply {
|
|
||||||
return nil, errors.New("reply type is not MultiReply")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(r.Elems)%2 != 0 {
|
|
||||||
return nil, errors.New("reply has odd number of elements")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(r.Elems)/2; i++ {
|
|
||||||
var val string
|
|
||||||
|
|
||||||
key, err := r.Elems[i*2].Str()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("key element has no string reply")
|
|
||||||
}
|
|
||||||
|
|
||||||
v := r.Elems[i*2+1]
|
|
||||||
if v.Type == BulkReply {
|
|
||||||
val = string(v.buf)
|
|
||||||
rmap[key] = val
|
|
||||||
} else if v.Type == NilReply {
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("value element type is not BulkReply or NilReply")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rmap, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of the reply and its sub-replies.
|
|
||||||
// This method is for debugging.
|
|
||||||
// Use method Reply.Str() for reading string reply.
|
|
||||||
func (r *Reply) String() string {
|
|
||||||
switch r.Type {
|
|
||||||
case ErrorReply:
|
|
||||||
return r.Err.Error()
|
|
||||||
case StatusReply:
|
|
||||||
fallthrough
|
|
||||||
case BulkReply:
|
|
||||||
return string(r.buf)
|
|
||||||
case IntegerReply:
|
|
||||||
return strconv.FormatInt(r.int, 10)
|
|
||||||
case NilReply:
|
|
||||||
return "<nil>"
|
|
||||||
case MultiReply:
|
|
||||||
s := "[ "
|
|
||||||
for _, e := range r.Elems {
|
|
||||||
s = s + e.String() + " "
|
|
||||||
}
|
|
||||||
return s + "]"
|
|
||||||
}
|
|
||||||
|
|
||||||
// This should never execute
|
|
||||||
return ""
|
|
||||||
}
|
|
125
Godeps/_workspace/src/github.com/fzzy/radix/redis/reply_test.go
generated
vendored
125
Godeps/_workspace/src/github.com/fzzy/radix/redis/reply_test.go
generated
vendored
@ -1,125 +0,0 @@
|
|||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
. "testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStr(t *T) {
|
|
||||||
r := &Reply{Type: ErrorReply, Err: LoadingError}
|
|
||||||
_, err := r.Str()
|
|
||||||
assert.Equal(t, LoadingError, err)
|
|
||||||
|
|
||||||
r = &Reply{Type: IntegerReply}
|
|
||||||
_, err = r.Str()
|
|
||||||
assert.NotNil(t, err)
|
|
||||||
|
|
||||||
r = &Reply{Type: StatusReply, buf: []byte("foo")}
|
|
||||||
b, err := r.Str()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, "foo", b)
|
|
||||||
|
|
||||||
r = &Reply{Type: BulkReply, buf: []byte("foo")}
|
|
||||||
b, err = r.Str()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, "foo", b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBytes(t *T) {
|
|
||||||
r := &Reply{Type: BulkReply, buf: []byte("foo")}
|
|
||||||
b, err := r.Bytes()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, []byte("foo"), b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInt64(t *T) {
|
|
||||||
r := &Reply{Type: ErrorReply, Err: LoadingError}
|
|
||||||
_, err := r.Int64()
|
|
||||||
assert.Equal(t, LoadingError, err)
|
|
||||||
|
|
||||||
r = &Reply{Type: IntegerReply, int: 5}
|
|
||||||
b, err := r.Int64()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, int64(5), b)
|
|
||||||
|
|
||||||
r = &Reply{Type: BulkReply, buf: []byte("5")}
|
|
||||||
b, err = r.Int64()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, int64(5), b)
|
|
||||||
|
|
||||||
r = &Reply{Type: BulkReply, buf: []byte("foo")}
|
|
||||||
_, err = r.Int64()
|
|
||||||
assert.NotNil(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInt(t *T) {
|
|
||||||
r := &Reply{Type: IntegerReply, int: 5}
|
|
||||||
b, err := r.Int()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, 5, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBool(t *T) {
|
|
||||||
r := &Reply{Type: IntegerReply, int: 0}
|
|
||||||
b, err := r.Bool()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, false, b)
|
|
||||||
|
|
||||||
r = &Reply{Type: StatusReply, buf: []byte("0")}
|
|
||||||
b, err = r.Bool()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, false, b)
|
|
||||||
|
|
||||||
r = &Reply{Type: IntegerReply, int: 2}
|
|
||||||
b, err = r.Bool()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, true, b)
|
|
||||||
|
|
||||||
r = &Reply{Type: NilReply}
|
|
||||||
_, err = r.Bool()
|
|
||||||
assert.NotNil(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestList(t *T) {
|
|
||||||
r := &Reply{Type: MultiReply}
|
|
||||||
r.Elems = make([]*Reply, 3)
|
|
||||||
r.Elems[0] = &Reply{Type: BulkReply, buf: []byte("0")}
|
|
||||||
r.Elems[1] = &Reply{Type: NilReply}
|
|
||||||
r.Elems[2] = &Reply{Type: BulkReply, buf: []byte("2")}
|
|
||||||
l, err := r.List()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, 3, len(l))
|
|
||||||
assert.Equal(t, "0", l[0])
|
|
||||||
assert.Equal(t, "", l[1])
|
|
||||||
assert.Equal(t, "2", l[2])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBytesList(t *T) {
|
|
||||||
r := &Reply{Type: MultiReply}
|
|
||||||
r.Elems = make([]*Reply, 3)
|
|
||||||
r.Elems[0] = &Reply{Type: BulkReply, buf: []byte("0")}
|
|
||||||
r.Elems[1] = &Reply{Type: NilReply}
|
|
||||||
r.Elems[2] = &Reply{Type: BulkReply, buf: []byte("2")}
|
|
||||||
l, err := r.ListBytes()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, 3, len(l))
|
|
||||||
assert.Equal(t, []byte("0"), l[0])
|
|
||||||
assert.Nil(t, l[1])
|
|
||||||
assert.Equal(t, []byte("2"), l[2])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHash(t *T) {
|
|
||||||
r := &Reply{Type: MultiReply}
|
|
||||||
r.Elems = make([]*Reply, 6)
|
|
||||||
r.Elems[0] = &Reply{Type: BulkReply, buf: []byte("a")}
|
|
||||||
r.Elems[1] = &Reply{Type: BulkReply, buf: []byte("0")}
|
|
||||||
r.Elems[2] = &Reply{Type: BulkReply, buf: []byte("b")}
|
|
||||||
r.Elems[3] = &Reply{Type: NilReply}
|
|
||||||
r.Elems[4] = &Reply{Type: BulkReply, buf: []byte("c")}
|
|
||||||
r.Elems[5] = &Reply{Type: BulkReply, buf: []byte("2")}
|
|
||||||
h, err := r.Hash()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, "0", h["a"])
|
|
||||||
assert.Equal(t, "", h["b"])
|
|
||||||
assert.Equal(t, "2", h["c"])
|
|
||||||
}
|
|
466
Godeps/_workspace/src/github.com/fzzy/radix/redis/resp/resp.go
generated
vendored
466
Godeps/_workspace/src/github.com/fzzy/radix/redis/resp/resp.go
generated
vendored
@ -1,466 +0,0 @@
|
|||||||
// This package provides an easy to use interface for creating and parsing
|
|
||||||
// messages encoded in the REdis Serialization Protocol (RESP). You can check
|
|
||||||
// out more details about the protocol here: http://redis.io/topics/protocol
|
|
||||||
package resp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
delim = []byte{'\r', '\n'}
|
|
||||||
delimEnd = delim[len(delim)-1]
|
|
||||||
)
|
|
||||||
|
|
||||||
type Type int
|
|
||||||
|
|
||||||
const (
|
|
||||||
SimpleStr Type = iota
|
|
||||||
Err
|
|
||||||
Int
|
|
||||||
BulkStr
|
|
||||||
Array
|
|
||||||
Nil
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
simpleStrPrefix = '+'
|
|
||||||
errPrefix = '-'
|
|
||||||
intPrefix = ':'
|
|
||||||
bulkStrPrefix = '$'
|
|
||||||
arrayPrefix = '*'
|
|
||||||
)
|
|
||||||
|
|
||||||
// Parse errors
|
|
||||||
var (
|
|
||||||
badType = errors.New("wrong type")
|
|
||||||
parseErr = errors.New("parse error")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Message struct {
|
|
||||||
Type
|
|
||||||
val interface{}
|
|
||||||
raw []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMessagePParses the given raw message and returns a Message struct
|
|
||||||
// representing it
|
|
||||||
func NewMessage(b []byte) (*Message, error) {
|
|
||||||
return ReadMessage(bytes.NewReader(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can be used when writing to a resp stream to write a simple-string-style
|
|
||||||
// stream (e.g. +OK\r\n) instead of the default bulk-string-style strings.
|
|
||||||
//
|
|
||||||
// foo := NewSimpleString("foo")
|
|
||||||
// bar := NewSimpleString("bar")
|
|
||||||
// baz := NewSimpleString("baz")
|
|
||||||
// resp.WriteArbitrary(w, foo)
|
|
||||||
// resp.WriteArbitrary(w, []interface{}{bar, baz})
|
|
||||||
//
|
|
||||||
func NewSimpleString(s string) *Message {
|
|
||||||
b := append(make([]byte, 0, len(s) + 3), '+')
|
|
||||||
b = append(b, []byte(s)...)
|
|
||||||
b = append(b, '\r', '\n')
|
|
||||||
return &Message{
|
|
||||||
Type: SimpleStr,
|
|
||||||
val: s,
|
|
||||||
raw: b,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadMessage attempts to read a message object from the given io.Reader, parse
|
|
||||||
// it, and return a Message struct representing it
|
|
||||||
func ReadMessage(reader io.Reader) (*Message, error) {
|
|
||||||
r := bufio.NewReader(reader)
|
|
||||||
return bufioReadMessage(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func bufioReadMessage(r *bufio.Reader) (*Message, error) {
|
|
||||||
b, err := r.Peek(1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch b[0] {
|
|
||||||
case simpleStrPrefix:
|
|
||||||
return readSimpleStr(r)
|
|
||||||
case errPrefix:
|
|
||||||
return readError(r)
|
|
||||||
case intPrefix:
|
|
||||||
return readInt(r)
|
|
||||||
case bulkStrPrefix:
|
|
||||||
return readBulkStr(r)
|
|
||||||
case arrayPrefix:
|
|
||||||
return readArray(r)
|
|
||||||
default:
|
|
||||||
return nil, badType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func readSimpleStr(r *bufio.Reader) (*Message, error) {
|
|
||||||
b, err := r.ReadBytes(delimEnd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &Message{Type: SimpleStr, val: b[1 : len(b)-2], raw: b}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readError(r *bufio.Reader) (*Message, error) {
|
|
||||||
b, err := r.ReadBytes(delimEnd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &Message{Type: Err, val: b[1 : len(b)-2], raw: b}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readInt(r *bufio.Reader) (*Message, error) {
|
|
||||||
b, err := r.ReadBytes(delimEnd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
i, err := strconv.ParseInt(string(b[1:len(b)-2]), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, parseErr
|
|
||||||
}
|
|
||||||
return &Message{Type: Int, val: i, raw: b}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readBulkStr(r *bufio.Reader) (*Message, error) {
|
|
||||||
b, err := r.ReadBytes(delimEnd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
size, err := strconv.ParseInt(string(b[1:len(b)-2]), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, parseErr
|
|
||||||
}
|
|
||||||
if size < 0 {
|
|
||||||
return &Message{Type: Nil, raw: b}, nil
|
|
||||||
}
|
|
||||||
total := make([]byte, size)
|
|
||||||
b2 := total
|
|
||||||
var n int
|
|
||||||
for len(b2) > 0 {
|
|
||||||
n, err = r.Read(b2)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
b2 = b2[n:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// There's a hanging \r\n there, gotta read past it
|
|
||||||
trail := make([]byte, 2)
|
|
||||||
for i := 0; i < 2; i++ {
|
|
||||||
if c, err := r.ReadByte(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
trail[i] = c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blens := len(b) + len(total)
|
|
||||||
raw := make([]byte, 0, blens+2)
|
|
||||||
raw = append(raw, b...)
|
|
||||||
raw = append(raw, total...)
|
|
||||||
raw = append(raw, trail...)
|
|
||||||
return &Message{Type: BulkStr, val: total, raw: raw}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readArray(r *bufio.Reader) (*Message, error) {
|
|
||||||
b, err := r.ReadBytes(delimEnd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
size, err := strconv.ParseInt(string(b[1:len(b)-2]), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, parseErr
|
|
||||||
}
|
|
||||||
if size < 0 {
|
|
||||||
return &Message{Type: Nil, raw: b}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
arr := make([]*Message, size)
|
|
||||||
for i := range arr {
|
|
||||||
m, err := bufioReadMessage(r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
arr[i] = m
|
|
||||||
b = append(b, m.raw...)
|
|
||||||
}
|
|
||||||
return &Message{Type: Array, val: arr, raw: b}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns a byte slice representing the value of the Message. Only valid
|
|
||||||
// for a Message of type SimpleStr, Err, and BulkStr. Others will return an
|
|
||||||
// error
|
|
||||||
func (m *Message) Bytes() ([]byte, error) {
|
|
||||||
if b, ok := m.val.([]byte); ok {
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
return nil, badType
|
|
||||||
}
|
|
||||||
|
|
||||||
// Str is a Convenience method around Bytes which converts the output to a
|
|
||||||
// string
|
|
||||||
func (m *Message) Str() (string, error) {
|
|
||||||
b, err := m.Bytes()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(b), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int returns an int64 representing the value of the Message. Only valid for
|
|
||||||
// Int messages
|
|
||||||
func (m *Message) Int() (int64, error) {
|
|
||||||
if i, ok := m.val.(int64); ok {
|
|
||||||
return i, nil
|
|
||||||
}
|
|
||||||
return 0, badType
|
|
||||||
}
|
|
||||||
|
|
||||||
// Err returns an error representing the value of the Message. Only valid for
|
|
||||||
// Err messages
|
|
||||||
func (m *Message) Err() (error, error) {
|
|
||||||
if m.Type != Err {
|
|
||||||
return nil, badType
|
|
||||||
}
|
|
||||||
s, err := m.Str()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return errors.New(s), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Array returns the Message slice encompassed by this Messsage, assuming the
|
|
||||||
// Message is of type Array
|
|
||||||
func (m *Message) Array() ([]*Message, error) {
|
|
||||||
if a, ok := m.val.([]*Message); ok {
|
|
||||||
return a, nil
|
|
||||||
}
|
|
||||||
return nil, badType
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteMessage takes in the given Message and writes its encoded form to the
|
|
||||||
// given io.Writer
|
|
||||||
func WriteMessage(w io.Writer, m *Message) error {
|
|
||||||
_, err := w.Write(m.raw)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteArbitrary takes in any primitive golang value, or Message, and writes
|
|
||||||
// its encoded form to the given io.Writer, inferring types where appropriate.
|
|
||||||
func WriteArbitrary(w io.Writer, m interface{}) error {
|
|
||||||
b := format(m, false)
|
|
||||||
_, err := w.Write(b)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteArbitraryAsString is similar to WriteArbitraryAsFlattenedString except
|
|
||||||
// that it won't flatten any embedded arrays.
|
|
||||||
func WriteArbitraryAsString(w io.Writer, m interface{}) error {
|
|
||||||
b := format(m, true)
|
|
||||||
_, err := w.Write(b)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteArbitraryAsFlattenedStrings is similar to WriteArbitrary except that it
|
|
||||||
// will encode all types except Array as a BulkStr, converting the argument into
|
|
||||||
// a string first as necessary. It will also flatten any embedded arrays into a
|
|
||||||
// single long array. This is useful because commands to a redis server must be
|
|
||||||
// given as an array of bulk strings. If the argument isn't already in a slice
|
|
||||||
// or map it will be wrapped so that it is written as an Array of size one.
|
|
||||||
//
|
|
||||||
// Note that if a Message type is found it will *not* be encoded to a BulkStr,
|
|
||||||
// but will simply be passed through as whatever type it already represents.
|
|
||||||
func WriteArbitraryAsFlattenedStrings(w io.Writer, m interface{}) error {
|
|
||||||
fm := flatten(m)
|
|
||||||
return WriteArbitraryAsString(w, fm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func format(m interface{}, forceString bool) []byte {
|
|
||||||
switch mt := m.(type) {
|
|
||||||
case []byte:
|
|
||||||
return formatStr(mt)
|
|
||||||
case string:
|
|
||||||
return formatStr([]byte(mt))
|
|
||||||
case bool:
|
|
||||||
if mt {
|
|
||||||
return formatStr([]byte("1"))
|
|
||||||
} else {
|
|
||||||
return formatStr([]byte("0"))
|
|
||||||
}
|
|
||||||
case nil:
|
|
||||||
if forceString {
|
|
||||||
return formatStr([]byte{})
|
|
||||||
} else {
|
|
||||||
return formatNil()
|
|
||||||
}
|
|
||||||
case int:
|
|
||||||
return formatInt(int64(mt), forceString)
|
|
||||||
case int8:
|
|
||||||
return formatInt(int64(mt), forceString)
|
|
||||||
case int16:
|
|
||||||
return formatInt(int64(mt), forceString)
|
|
||||||
case int32:
|
|
||||||
return formatInt(int64(mt), forceString)
|
|
||||||
case int64:
|
|
||||||
return formatInt(mt, forceString)
|
|
||||||
case uint:
|
|
||||||
return formatInt(int64(mt), forceString)
|
|
||||||
case uint8:
|
|
||||||
return formatInt(int64(mt), forceString)
|
|
||||||
case uint16:
|
|
||||||
return formatInt(int64(mt), forceString)
|
|
||||||
case uint32:
|
|
||||||
return formatInt(int64(mt), forceString)
|
|
||||||
case uint64:
|
|
||||||
return formatInt(int64(mt), forceString)
|
|
||||||
case float32:
|
|
||||||
ft := strconv.FormatFloat(float64(mt), 'f', -1, 32)
|
|
||||||
return formatStr([]byte(ft))
|
|
||||||
case float64:
|
|
||||||
ft := strconv.FormatFloat(mt, 'f', -1, 64)
|
|
||||||
return formatStr([]byte(ft))
|
|
||||||
case error:
|
|
||||||
if forceString {
|
|
||||||
return formatStr([]byte(mt.Error()))
|
|
||||||
} else {
|
|
||||||
return formatErr(mt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We duplicate the below code here a bit, since this is the common case and
|
|
||||||
// it'd be better to not get the reflect package involved here
|
|
||||||
case []interface{}:
|
|
||||||
l := len(mt)
|
|
||||||
b := make([]byte, 0, l*1024)
|
|
||||||
b = append(b, '*')
|
|
||||||
b = append(b, []byte(strconv.Itoa(l))...)
|
|
||||||
b = append(b, []byte("\r\n")...)
|
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
b = append(b, format(mt[i], forceString)...)
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
|
|
||||||
case *Message:
|
|
||||||
return mt.raw
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Fallback to reflect-based.
|
|
||||||
switch reflect.TypeOf(m).Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
rm := reflect.ValueOf(mt)
|
|
||||||
l := rm.Len()
|
|
||||||
b := make([]byte, 0, l*1024)
|
|
||||||
b = append(b, '*')
|
|
||||||
b = append(b, []byte(strconv.Itoa(l))...)
|
|
||||||
b = append(b, []byte("\r\n")...)
|
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
vv := rm.Index(i).Interface()
|
|
||||||
b = append(b, format(vv, forceString)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return b
|
|
||||||
case reflect.Map:
|
|
||||||
rm := reflect.ValueOf(mt)
|
|
||||||
l := rm.Len() * 2
|
|
||||||
b := make([]byte, 0, l*1024)
|
|
||||||
b = append(b, '*')
|
|
||||||
b = append(b, []byte(strconv.Itoa(l))...)
|
|
||||||
b = append(b, []byte("\r\n")...)
|
|
||||||
keys := rm.MapKeys()
|
|
||||||
for _, k := range keys {
|
|
||||||
kv := k.Interface()
|
|
||||||
vv := rm.MapIndex(k).Interface()
|
|
||||||
b = append(b, format(kv, forceString)...)
|
|
||||||
b = append(b, format(vv, forceString)...)
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
default:
|
|
||||||
return formatStr([]byte(fmt.Sprint(m)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var typeOfBytes = reflect.TypeOf([]byte(nil))
|
|
||||||
|
|
||||||
func flatten(m interface{}) []interface{} {
|
|
||||||
t := reflect.TypeOf(m)
|
|
||||||
|
|
||||||
// If it's a byte-slice we don't want to flatten
|
|
||||||
if t == typeOfBytes {
|
|
||||||
return []interface{}{m}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch t.Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
rm := reflect.ValueOf(m)
|
|
||||||
l := rm.Len()
|
|
||||||
ret := make([]interface{}, 0, l)
|
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
ret = append(ret, flatten(rm.Index(i).Interface())...)
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
|
|
||||||
case reflect.Map:
|
|
||||||
rm := reflect.ValueOf(m)
|
|
||||||
l := rm.Len() * 2
|
|
||||||
keys := rm.MapKeys()
|
|
||||||
ret := make([]interface{}, 0, l)
|
|
||||||
for _, k := range keys {
|
|
||||||
kv := k.Interface()
|
|
||||||
vv := rm.MapIndex(k).Interface()
|
|
||||||
ret = append(ret, flatten(kv)...)
|
|
||||||
ret = append(ret, flatten(vv)...)
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
|
|
||||||
default:
|
|
||||||
return []interface{}{m}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatStr(b []byte) []byte {
|
|
||||||
l := strconv.Itoa(len(b))
|
|
||||||
bs := make([]byte, 0, len(l)+len(b)+5)
|
|
||||||
bs = append(bs, bulkStrPrefix)
|
|
||||||
bs = append(bs, []byte(l)...)
|
|
||||||
bs = append(bs, delim...)
|
|
||||||
bs = append(bs, b...)
|
|
||||||
bs = append(bs, delim...)
|
|
||||||
return bs
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatErr(ierr error) []byte {
|
|
||||||
ierrstr := []byte(ierr.Error())
|
|
||||||
bs := make([]byte, 0, len(ierrstr)+3)
|
|
||||||
bs = append(bs, errPrefix)
|
|
||||||
bs = append(bs, ierrstr...)
|
|
||||||
bs = append(bs, delim...)
|
|
||||||
return bs
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatInt(i int64, forceString bool) []byte {
|
|
||||||
istr := strconv.FormatInt(i, 10)
|
|
||||||
if forceString {
|
|
||||||
return formatStr([]byte(istr))
|
|
||||||
}
|
|
||||||
bs := make([]byte, 0, len(istr)+3)
|
|
||||||
bs = append(bs, intPrefix)
|
|
||||||
bs = append(bs, istr...)
|
|
||||||
bs = append(bs, delim...)
|
|
||||||
return bs
|
|
||||||
}
|
|
||||||
|
|
||||||
var nilFormatted = []byte("$-1\r\n")
|
|
||||||
|
|
||||||
func formatNil() []byte {
|
|
||||||
return nilFormatted
|
|
||||||
}
|
|
209
Godeps/_workspace/src/github.com/fzzy/radix/redis/resp/resp_test.go
generated
vendored
209
Godeps/_workspace/src/github.com/fzzy/radix/redis/resp/resp_test.go
generated
vendored
@ -1,209 +0,0 @@
|
|||||||
package resp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
. "testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRead(t *T) {
|
|
||||||
var m *Message
|
|
||||||
var err error
|
|
||||||
|
|
||||||
_, err = NewMessage(nil)
|
|
||||||
assert.NotNil(t, err)
|
|
||||||
|
|
||||||
_, err = NewMessage([]byte{})
|
|
||||||
assert.NotNil(t, err)
|
|
||||||
|
|
||||||
// Simple string
|
|
||||||
m, _ = NewMessage([]byte("+ohey\r\n"))
|
|
||||||
assert.Equal(t, SimpleStr, m.Type)
|
|
||||||
assert.Equal(t, []byte("ohey"), m.val)
|
|
||||||
|
|
||||||
// Empty simple string
|
|
||||||
m, _ = NewMessage([]byte("+\r\n"))
|
|
||||||
assert.Equal(t, SimpleStr, m.Type)
|
|
||||||
assert.Equal(t, []byte(""), m.val.([]byte))
|
|
||||||
|
|
||||||
// Error
|
|
||||||
m, _ = NewMessage([]byte("-ohey\r\n"))
|
|
||||||
assert.Equal(t, Err, m.Type)
|
|
||||||
assert.Equal(t, []byte("ohey"), m.val.([]byte))
|
|
||||||
|
|
||||||
// Empty error
|
|
||||||
m, _ = NewMessage([]byte("-\r\n"))
|
|
||||||
assert.Equal(t, Err, m.Type)
|
|
||||||
assert.Equal(t, []byte(""), m.val.([]byte))
|
|
||||||
|
|
||||||
// Int
|
|
||||||
m, _ = NewMessage([]byte(":1024\r\n"))
|
|
||||||
assert.Equal(t, Int, m.Type)
|
|
||||||
assert.Equal(t, int64(1024), m.val.(int64))
|
|
||||||
|
|
||||||
// Bulk string
|
|
||||||
m, _ = NewMessage([]byte("$3\r\nfoo\r\n"))
|
|
||||||
assert.Equal(t, BulkStr, m.Type)
|
|
||||||
assert.Equal(t, []byte("foo"), m.val.([]byte))
|
|
||||||
|
|
||||||
// Empty bulk string
|
|
||||||
m, _ = NewMessage([]byte("$0\r\n\r\n"))
|
|
||||||
assert.Equal(t, BulkStr, m.Type)
|
|
||||||
assert.Equal(t, []byte(""), m.val.([]byte))
|
|
||||||
|
|
||||||
// Nil bulk string
|
|
||||||
m, _ = NewMessage([]byte("$-1\r\n"))
|
|
||||||
assert.Equal(t, Nil, m.Type)
|
|
||||||
|
|
||||||
// Array
|
|
||||||
m, _ = NewMessage([]byte("*2\r\n+foo\r\n+bar\r\n"))
|
|
||||||
assert.Equal(t, Array, m.Type)
|
|
||||||
assert.Equal(t, 2, len(m.val.([]*Message)))
|
|
||||||
assert.Equal(t, SimpleStr, m.val.([]*Message)[0].Type)
|
|
||||||
assert.Equal(t, []byte("foo"), m.val.([]*Message)[0].val.([]byte))
|
|
||||||
assert.Equal(t, SimpleStr, m.val.([]*Message)[1].Type)
|
|
||||||
assert.Equal(t, []byte("bar"), m.val.([]*Message)[1].val.([]byte))
|
|
||||||
|
|
||||||
// Empty array
|
|
||||||
m, _ = NewMessage([]byte("*0\r\n"))
|
|
||||||
assert.Equal(t, Array, m.Type)
|
|
||||||
assert.Equal(t, 0, len(m.val.([]*Message)))
|
|
||||||
|
|
||||||
// Nil Array
|
|
||||||
m, _ = NewMessage([]byte("*-1\r\n"))
|
|
||||||
assert.Equal(t, Nil, m.Type)
|
|
||||||
|
|
||||||
// Embedded Array
|
|
||||||
m, _ = NewMessage([]byte("*3\r\n+foo\r\n+bar\r\n*2\r\n+foo\r\n+bar\r\n"))
|
|
||||||
assert.Equal(t, Array, m.Type)
|
|
||||||
assert.Equal(t, 3, len(m.val.([]*Message)))
|
|
||||||
assert.Equal(t, SimpleStr, m.val.([]*Message)[0].Type)
|
|
||||||
assert.Equal(t, []byte("foo"), m.val.([]*Message)[0].val.([]byte))
|
|
||||||
assert.Equal(t, SimpleStr, m.val.([]*Message)[1].Type)
|
|
||||||
assert.Equal(t, []byte("bar"), m.val.([]*Message)[1].val.([]byte))
|
|
||||||
m = m.val.([]*Message)[2]
|
|
||||||
assert.Equal(t, 2, len(m.val.([]*Message)))
|
|
||||||
assert.Equal(t, SimpleStr, m.val.([]*Message)[0].Type)
|
|
||||||
assert.Equal(t, []byte("foo"), m.val.([]*Message)[0].val.([]byte))
|
|
||||||
assert.Equal(t, SimpleStr, m.val.([]*Message)[1].Type)
|
|
||||||
assert.Equal(t, []byte("bar"), m.val.([]*Message)[1].val.([]byte))
|
|
||||||
|
|
||||||
// Test that two bulks in a row read correctly
|
|
||||||
m, _ = NewMessage([]byte("*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"))
|
|
||||||
assert.Equal(t, Array, m.Type)
|
|
||||||
assert.Equal(t, 2, len(m.val.([]*Message)))
|
|
||||||
assert.Equal(t, BulkStr, m.val.([]*Message)[0].Type)
|
|
||||||
assert.Equal(t, []byte("foo"), m.val.([]*Message)[0].val.([]byte))
|
|
||||||
assert.Equal(t, BulkStr, m.val.([]*Message)[1].Type)
|
|
||||||
assert.Equal(t, []byte("bar"), m.val.([]*Message)[1].val.([]byte))
|
|
||||||
}
|
|
||||||
|
|
||||||
type arbitraryTest struct {
|
|
||||||
val interface{}
|
|
||||||
expect []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
var nilMessage, _ = NewMessage([]byte("$-1\r\n"))
|
|
||||||
|
|
||||||
var arbitraryTests = []arbitraryTest{
|
|
||||||
{[]byte("OHAI"), []byte("$4\r\nOHAI\r\n")},
|
|
||||||
{"OHAI", []byte("$4\r\nOHAI\r\n")},
|
|
||||||
{true, []byte("$1\r\n1\r\n")},
|
|
||||||
{false, []byte("$1\r\n0\r\n")},
|
|
||||||
{nil, []byte("$-1\r\n")},
|
|
||||||
{80, []byte(":80\r\n")},
|
|
||||||
{int64(-80), []byte(":-80\r\n")},
|
|
||||||
{uint64(80), []byte(":80\r\n")},
|
|
||||||
{float32(0.1234), []byte("$6\r\n0.1234\r\n")},
|
|
||||||
{float64(0.1234), []byte("$6\r\n0.1234\r\n")},
|
|
||||||
{errors.New("hi"), []byte("-hi\r\n")},
|
|
||||||
|
|
||||||
{nilMessage, []byte("$-1\r\n")},
|
|
||||||
|
|
||||||
{[]int{1, 2, 3}, []byte("*3\r\n:1\r\n:2\r\n:3\r\n")},
|
|
||||||
{map[int]int{1: 2}, []byte("*2\r\n:1\r\n:2\r\n")},
|
|
||||||
|
|
||||||
{NewSimpleString("OK"), []byte("+OK\r\n")},
|
|
||||||
}
|
|
||||||
|
|
||||||
var arbitraryAsStringTests = []arbitraryTest{
|
|
||||||
{[]byte("OHAI"), []byte("$4\r\nOHAI\r\n")},
|
|
||||||
{"OHAI", []byte("$4\r\nOHAI\r\n")},
|
|
||||||
{true, []byte("$1\r\n1\r\n")},
|
|
||||||
{false, []byte("$1\r\n0\r\n")},
|
|
||||||
{nil, []byte("$0\r\n\r\n")},
|
|
||||||
{80, []byte("$2\r\n80\r\n")},
|
|
||||||
{int64(-80), []byte("$3\r\n-80\r\n")},
|
|
||||||
{uint64(80), []byte("$2\r\n80\r\n")},
|
|
||||||
{float32(0.1234), []byte("$6\r\n0.1234\r\n")},
|
|
||||||
{float64(0.1234), []byte("$6\r\n0.1234\r\n")},
|
|
||||||
{errors.New("hi"), []byte("$2\r\nhi\r\n")},
|
|
||||||
|
|
||||||
{nilMessage, []byte("$-1\r\n")},
|
|
||||||
|
|
||||||
{[]int{1, 2, 3}, []byte("*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n")},
|
|
||||||
{map[int]int{1: 2}, []byte("*2\r\n$1\r\n1\r\n$1\r\n2\r\n")},
|
|
||||||
|
|
||||||
{NewSimpleString("OK"), []byte("+OK\r\n")},
|
|
||||||
}
|
|
||||||
|
|
||||||
var arbitraryAsFlattenedStringsTests = []arbitraryTest{
|
|
||||||
{
|
|
||||||
[]interface{}{"wat", map[string]interface{}{
|
|
||||||
"foo": 1,
|
|
||||||
}},
|
|
||||||
[]byte("*3\r\n$3\r\nwat\r\n$3\r\nfoo\r\n$1\r\n1\r\n"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWriteArbitrary(t *T) {
|
|
||||||
var err error
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
|
||||||
for _, test := range arbitraryTests {
|
|
||||||
t.Logf("Checking test %v", test)
|
|
||||||
buf.Reset()
|
|
||||||
err = WriteArbitrary(buf, test.val)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, test.expect, buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWriteArbitraryAsString(t *T) {
|
|
||||||
var err error
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
|
||||||
for _, test := range arbitraryAsStringTests {
|
|
||||||
t.Logf("Checking test %v", test)
|
|
||||||
buf.Reset()
|
|
||||||
err = WriteArbitraryAsString(buf, test.val)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, test.expect, buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWriteArbitraryAsFlattenedStrings(t *T) {
|
|
||||||
var err error
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
|
||||||
for _, test := range arbitraryAsFlattenedStringsTests {
|
|
||||||
t.Logf("Checking test %v", test)
|
|
||||||
buf.Reset()
|
|
||||||
err = WriteArbitraryAsFlattenedStrings(buf, test.val)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, test.expect, buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMessageWrite(t *T) {
|
|
||||||
var err error
|
|
||||||
var m *Message
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
|
||||||
for _, test := range arbitraryTests {
|
|
||||||
t.Logf("Checking test; %v", test)
|
|
||||||
buf.Reset()
|
|
||||||
m, err = NewMessage(test.expect)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
err = WriteMessage(buf, m)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, test.expect, buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
84
thirdparty/redis-datastore/datastore.go
vendored
84
thirdparty/redis-datastore/datastore.go
vendored
@ -1,84 +0,0 @@
|
|||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/fzzy/radix/redis"
|
|
||||||
datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
|
|
||||||
query "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ datastore.Datastore = &Datastore{}
|
|
||||||
var _ datastore.ThreadSafeDatastore = &Datastore{}
|
|
||||||
|
|
||||||
var ErrInvalidType = errors.New("redis datastore: invalid type error. this datastore only supports []byte values")
|
|
||||||
|
|
||||||
func NewExpiringDatastore(client *redis.Client, ttl time.Duration) (datastore.ThreadSafeDatastore, error) {
|
|
||||||
return &Datastore{
|
|
||||||
client: client,
|
|
||||||
ttl: ttl,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDatastore(client *redis.Client) (datastore.ThreadSafeDatastore, error) {
|
|
||||||
return &Datastore{
|
|
||||||
client: client,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Datastore struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
client *redis.Client
|
|
||||||
ttl time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ds *Datastore) Put(key datastore.Key, value interface{}) error {
|
|
||||||
ds.mu.Lock()
|
|
||||||
defer ds.mu.Unlock()
|
|
||||||
|
|
||||||
data, ok := value.([]byte)
|
|
||||||
if !ok {
|
|
||||||
return ErrInvalidType
|
|
||||||
}
|
|
||||||
|
|
||||||
ds.client.Append("SET", key.String(), data)
|
|
||||||
if ds.ttl != 0 {
|
|
||||||
ds.client.Append("EXPIRE", key.String(), ds.ttl.Seconds())
|
|
||||||
}
|
|
||||||
if err := ds.client.GetReply().Err; err != nil {
|
|
||||||
return fmt.Errorf("failed to put value: %s", err)
|
|
||||||
}
|
|
||||||
if ds.ttl != 0 {
|
|
||||||
if err := ds.client.GetReply().Err; err != nil {
|
|
||||||
return fmt.Errorf("failed to set expiration: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ds *Datastore) Get(key datastore.Key) (value interface{}, err error) {
|
|
||||||
ds.mu.Lock()
|
|
||||||
defer ds.mu.Unlock()
|
|
||||||
return ds.client.Cmd("GET", key.String()).Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ds *Datastore) Has(key datastore.Key) (exists bool, err error) {
|
|
||||||
ds.mu.Lock()
|
|
||||||
defer ds.mu.Unlock()
|
|
||||||
return ds.client.Cmd("EXISTS", key.String()).Bool()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ds *Datastore) Delete(key datastore.Key) (err error) {
|
|
||||||
ds.mu.Lock()
|
|
||||||
defer ds.mu.Unlock()
|
|
||||||
return ds.client.Cmd("DEL", key.String()).Err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ds *Datastore) Query(q query.Query) (query.Results, error) {
|
|
||||||
return nil, errors.New("TODO implement query for redis datastore?")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ds *Datastore) IsThreadSafe() {}
|
|
108
thirdparty/redis-datastore/datastore_test.go
vendored
108
thirdparty/redis-datastore/datastore_test.go
vendored
@ -1,108 +0,0 @@
|
|||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/fzzy/radix/redis"
|
|
||||||
datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
|
|
||||||
"github.com/ipfs/go-ipfs/thirdparty/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
const RedisEnv = "REDIS_DATASTORE_TEST_HOST"
|
|
||||||
|
|
||||||
func TestPutGetBytes(t *testing.T) {
|
|
||||||
client := clientOrAbort(t)
|
|
||||||
ds, err := NewDatastore(client)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
key, val := datastore.NewKey("foo"), []byte("bar")
|
|
||||||
assert.Nil(ds.Put(key, val), t)
|
|
||||||
v, err := ds.Get(key)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if bytes.Compare(v.([]byte), val) != 0 {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHasBytes(t *testing.T) {
|
|
||||||
client := clientOrAbort(t)
|
|
||||||
ds, err := NewDatastore(client)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
key, val := datastore.NewKey("foo"), []byte("bar")
|
|
||||||
has, err := ds.Has(key)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if has {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Nil(ds.Put(key, val), t)
|
|
||||||
hasAfterPut, err := ds.Has(key)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !hasAfterPut {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
|
||||||
client := clientOrAbort(t)
|
|
||||||
ds, err := NewDatastore(client)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
key, val := datastore.NewKey("foo"), []byte("bar")
|
|
||||||
assert.Nil(ds.Put(key, val), t)
|
|
||||||
assert.Nil(ds.Delete(key), t)
|
|
||||||
|
|
||||||
hasAfterDelete, err := ds.Has(key)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if hasAfterDelete {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExpiry(t *testing.T) {
|
|
||||||
ttl := 1 * time.Second
|
|
||||||
client := clientOrAbort(t)
|
|
||||||
ds, err := NewExpiringDatastore(client, ttl)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
key, val := datastore.NewKey("foo"), []byte("bar")
|
|
||||||
assert.Nil(ds.Put(key, val), t)
|
|
||||||
time.Sleep(ttl + 1*time.Second)
|
|
||||||
assert.Nil(ds.Delete(key), t)
|
|
||||||
|
|
||||||
hasAfterExpiration, err := ds.Has(key)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if hasAfterExpiration {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func clientOrAbort(t *testing.T) *redis.Client {
|
|
||||||
c, err := redis.Dial("tcp", os.Getenv(RedisEnv))
|
|
||||||
if err != nil {
|
|
||||||
t.Log("could not connect to a redis instance")
|
|
||||||
t.SkipNow()
|
|
||||||
}
|
|
||||||
if err := c.Cmd("FLUSHALL").Err; err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
Reference in New Issue
Block a user