From a389b6ca04237548282bbee53cec630a3d8f017a Mon Sep 17 00:00:00 2001 From: tidwall Date: Tue, 3 Mar 2020 14:16:44 -0700 Subject: [PATCH] Added AppendAny --- append.go | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ redcon.go | 8 +++++ 2 files changed, 108 insertions(+) diff --git a/append.go b/append.go index c9397a0..b526c5d 100644 --- a/append.go +++ b/append.go @@ -1,6 +1,9 @@ package redcon import ( + "fmt" + "reflect" + "sort" "strconv" "strings" ) @@ -348,3 +351,100 @@ func AppendBulkUint(b []byte, x uint64) []byte { b = b[:mark1+(mark3-mark2)] return b } + +// AppendAny appends any type to valid Redis type +func AppendAny(b []byte, v interface{}) []byte { + switch v := v.(type) { + case nil: + b = AppendNull(b) + case error: + b = AppendError(b, "ERR "+v.Error()) + case string: + b = AppendBulkString(b, v) + case []byte: + b = AppendBulk(b, v) + case bool: + if v { + b = AppendInt(b, 1) + } else { + b = AppendInt(b, 0) + } + case int: + b = AppendInt(b, int64(v)) + case int8: + b = AppendInt(b, int64(v)) + case int16: + b = AppendInt(b, int64(v)) + case int32: + b = AppendInt(b, int64(v)) + case int64: + b = AppendInt(b, int64(v)) + case uint: + b = AppendUint(b, uint64(v)) + case uint8: + b = AppendUint(b, uint64(v)) + case uint16: + b = AppendUint(b, uint64(v)) + case uint32: + b = AppendUint(b, uint64(v)) + case uint64: + b = AppendUint(b, uint64(v)) + case float32: + b = AppendBulkFloat(b, float64(v)) + case float64: + b = AppendBulkFloat(b, float64(v)) + default: + vv := reflect.ValueOf(v) + switch vv.Kind() { + case reflect.Slice: + n := vv.Len() + b = AppendArray(b, n) + for i := 0; i < n; i++ { + b = AppendAny(b, vv.Index(i).Interface()) + } + case reflect.Map: + n := vv.Len() + b = AppendArray(b, n*2) + var i int + var strKey bool + var strsKeyItems []strKeyItem + + iter := vv.MapRange() + for iter.Next() { + key := iter.Key().Interface() + if i == 0 { + if _, ok := key.(string); ok { + strKey = true + strsKeyItems = make([]strKeyItem, n) + } + } + if strKey { + strsKeyItems[i] = strKeyItem{ + key.(string), iter.Value().Interface(), + } + } else { + b = AppendAny(b, key) + b = AppendAny(b, iter.Value().Interface()) + } + i++ + } + if strKey { + sort.Slice(strsKeyItems, func(i, j int) bool { + return strsKeyItems[i].key < strsKeyItems[j].key + }) + for _, item := range strsKeyItems { + b = AppendBulkString(b, item.key) + b = AppendAny(b, item.value) + } + } + default: + b = AppendBulkString(b, fmt.Sprint(v)) + } + } + return b +} + +type strKeyItem struct { + key string + value interface{} +} diff --git a/redcon.go b/redcon.go index d622fca..fb70f10 100644 --- a/redcon.go +++ b/redcon.go @@ -60,6 +60,8 @@ type Conn interface { WriteNull() // WriteRaw writes raw data to the client. WriteRaw(data []byte) + // WriteAny writes any type to the client. + WriteAny(any interface{}) // Context returns a user-defined context Context() interface{} // SetContext sets a user-defined context @@ -442,6 +444,7 @@ func (c *conn) WriteError(msg string) { c.wr.WriteError(msg) } func (c *conn) WriteArray(count int) { c.wr.WriteArray(count) } func (c *conn) WriteNull() { c.wr.WriteNull() } func (c *conn) WriteRaw(data []byte) { c.wr.WriteRaw(data) } +func (c *conn) WriteAny(v interface{}) { c.wr.WriteAny(v) } func (c *conn) RemoteAddr() string { return c.addr } func (c *conn) ReadPipeline() []Command { cmds := c.cmds @@ -633,6 +636,11 @@ func (w *Writer) WriteRaw(data []byte) { w.b = append(w.b, data...) } +// WriteAny writes any type to client. +func (w *Writer) WriteAny(v interface{}) { + w.b = AppendAny(w.b, v) +} + // Reader represent a reader for RESP or telnet commands. type Reader struct { rd *bufio.Reader