Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
dms3
go-datastore
Commits
0f0f9bfc
Commit
0f0f9bfc
authored
Jul 16, 2015
by
Jeromy
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
nobody needs to bisect
parent
72cf3024
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
1519 additions
and
3 deletions
+1519
-3
Godeps/Godeps.json
Godeps/Godeps.json
+5
-0
Godeps/_workspace/src/github.com/fzzy/radix/redis/client.go
Godeps/_workspace/src/github.com/fzzy/radix/redis/client.go
+244
-0
Godeps/_workspace/src/github.com/fzzy/radix/redis/client_test.go
..._workspace/src/github.com/fzzy/radix/redis/client_test.go
+106
-0
Godeps/_workspace/src/github.com/fzzy/radix/redis/doc.go
Godeps/_workspace/src/github.com/fzzy/radix/redis/doc.go
+87
-0
Godeps/_workspace/src/github.com/fzzy/radix/redis/reply.go
Godeps/_workspace/src/github.com/fzzy/radix/redis/reply.go
+275
-0
Godeps/_workspace/src/github.com/fzzy/radix/redis/reply_test.go
.../_workspace/src/github.com/fzzy/radix/redis/reply_test.go
+125
-0
Godeps/_workspace/src/github.com/fzzy/radix/redis/resp/resp.go
...s/_workspace/src/github.com/fzzy/radix/redis/resp/resp.go
+466
-0
Godeps/_workspace/src/github.com/fzzy/radix/redis/resp/resp_test.go
...rkspace/src/github.com/fzzy/radix/redis/resp/resp_test.go
+209
-0
redis/redis.go
redis/redis.go
+1
-2
redis/redis_test.go
redis/redis_test.go
+1
-1
No files found.
Godeps/Godeps.json
View file @
0f0f9bfc
...
@@ -21,6 +21,11 @@
...
@@ -21,6 +21,11 @@
"ImportPath"
:
"github.com/dustin/randbo"
,
"ImportPath"
:
"github.com/dustin/randbo"
,
"Rev"
:
"7f1b564ca7242d22bcc6e2128beb90d9fa38b9f0"
"Rev"
:
"7f1b564ca7242d22bcc6e2128beb90d9fa38b9f0"
},
},
{
"ImportPath"
:
"github.com/fzzy/radix/redis"
,
"Comment"
:
"v0.5.1"
,
"Rev"
:
"27a863cdffdb0998d13e1e11992b18489aeeaa25"
},
{
{
"ImportPath"
:
"github.com/hashicorp/golang-lru"
,
"ImportPath"
:
"github.com/hashicorp/golang-lru"
,
"Rev"
:
"4dfff096c4973178c8f35cf6dd1a732a0a139370"
"Rev"
:
"4dfff096c4973178c8f35cf6dd1a732a0a139370"
...
...
Godeps/_workspace/src/github.com/fzzy/radix/redis/client.go
0 → 100644
View file @
0f0f9bfc
package
redis
import
(
"bufio"
"errors"
"net"
"strings"
"time"
"github.com/jbenet/go-datastore/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
}
Godeps/_workspace/src/github.com/fzzy/radix/redis/client_test.go
0 → 100644
View file @
0f0f9bfc
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\n
foobar
\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\n
foobar
\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
)
}
Godeps/_workspace/src/github.com/fzzy/radix/redis/doc.go
0 → 100644
View file @
0f0f9bfc
// 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
Godeps/_workspace/src/github.com/fzzy/radix/redis/reply.go
0 → 100644
View file @
0f0f9bfc
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
""
}
Godeps/_workspace/src/github.com/fzzy/radix/redis/reply_test.go
0 → 100644
View file @
0f0f9bfc
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"
])
}
Godeps/_workspace/src/github.com/fzzy/radix/redis/resp/resp.go
0 → 100644
View file @
0f0f9bfc
// 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
}
Godeps/_workspace/src/github.com/fzzy/radix/redis/resp/resp_test.go
0 → 100644
View file @
0f0f9bfc
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\n
foo
\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\n
foo
\r\n
$3
\r\n
bar
\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\n
OHAI
\r\n
"
)},
{
"OHAI"
,
[]
byte
(
"$4
\r\n
OHAI
\r\n
"
)},
{
true
,
[]
byte
(
"$1
\r\n
1
\r\n
"
)},
{
false
,
[]
byte
(
"$1
\r\n
0
\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\n
0.1234
\r\n
"
)},
{
float64
(
0.1234
),
[]
byte
(
"$6
\r\n
0.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\n
OHAI
\r\n
"
)},
{
"OHAI"
,
[]
byte
(
"$4
\r\n
OHAI
\r\n
"
)},
{
true
,
[]
byte
(
"$1
\r\n
1
\r\n
"
)},
{
false
,
[]
byte
(
"$1
\r\n
0
\r\n
"
)},
{
nil
,
[]
byte
(
"$0
\r\n\r\n
"
)},
{
80
,
[]
byte
(
"$2
\r\n
80
\r\n
"
)},
{
int64
(
-
80
),
[]
byte
(
"$3
\r\n
-80
\r\n
"
)},
{
uint64
(
80
),
[]
byte
(
"$2
\r\n
80
\r\n
"
)},
{
float32
(
0.1234
),
[]
byte
(
"$6
\r\n
0.1234
\r\n
"
)},
{
float64
(
0.1234
),
[]
byte
(
"$6
\r\n
0.1234
\r\n
"
)},
{
errors
.
New
(
"hi"
),
[]
byte
(
"$2
\r\n
hi
\r\n
"
)},
{
nilMessage
,
[]
byte
(
"$-1
\r\n
"
)},
{[]
int
{
1
,
2
,
3
},
[]
byte
(
"*3
\r\n
$1
\r\n
1
\r\n
$1
\r\n
2
\r\n
$1
\r\n
3
\r\n
"
)},
{
map
[
int
]
int
{
1
:
2
},
[]
byte
(
"*2
\r\n
$1
\r\n
1
\r\n
$1
\r\n
2
\r\n
"
)},
{
NewSimpleString
(
"OK"
),
[]
byte
(
"+OK
\r\n
"
)},
}
var
arbitraryAsFlattenedStringsTests
=
[]
arbitraryTest
{
{
[]
interface
{}{
"wat"
,
map
[
string
]
interface
{}{
"foo"
:
1
,
}},
[]
byte
(
"*3
\r\n
$3
\r\n
wat
\r\n
$3
\r\n
foo
\r\n
$1
\r\n
1
\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
())
}
}
redis/redis.go
View file @
0f0f9bfc
...
@@ -6,9 +6,8 @@ import (
...
@@ -6,9 +6,8 @@ import (
"sync"
"sync"
"time"
"time"
"github.com/fzzy/radix/redis"
datastore
"github.com/jbenet/go-datastore"
datastore
"github.com/jbenet/go-datastore"
"github.com/jbenet/go-datastore/Godeps/_workspace/src/github.com/fzzy/radix/redis"
query
"github.com/jbenet/go-datastore/query"
query
"github.com/jbenet/go-datastore/query"
)
)
...
...
redis/redis_test.go
View file @
0f0f9bfc
...
@@ -6,8 +6,8 @@ import (
...
@@ -6,8 +6,8 @@ import (
"testing"
"testing"
"time"
"time"
"github.com/fzzy/radix/redis"
datastore
"github.com/jbenet/go-datastore"
datastore
"github.com/jbenet/go-datastore"
"github.com/jbenet/go-datastore/Godeps/_workspace/src/github.com/fzzy/radix/redis"
dstest
"github.com/jbenet/go-datastore/test"
dstest
"github.com/jbenet/go-datastore/test"
)
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment